Index: backend/pgsql.t ================================================================== --- backend/pgsql.t +++ backend/pgsql.t @@ -284,10 +284,34 @@ params = {uint64}, sql = [[ select (notice).* from pg_temp.parsavpg_notices where rcpt = $1::bigint ]]; }; + + circle_search = { + params = {uint64,uint64}, sql = [[ + select name, id, owner, array_length(members,1) from parsav_circles where + ($1::bigint = 0 or $1::bigint = owner) and + ($2::bigint = 0 or $2::bigint = id) + ]]; + }; + + circle_members_fetch_cid = { + params = {uint64, uint64}, sql = [[ + select unnest(members) from parsav_circles where + ($1::bigint = 0 or owner = $1::bigint) and + id = $2::bigint + ]]; + }; + + circle_members_fetch_name = { + params = {uint64, pstring}, sql = [[ + select unnest(members) from parsav_circles where + ($1::bigint = 0 or owner = $1::bigint) and + name = $2::text + ]]; + }; auth_sigtime_user_fetch = { params = {uint64}, sql = [[ select authtime::bigint from parsav_actors where id = $1::bigint @@ -2037,10 +2061,50 @@ end]; actor_conf_int_set = [terra(src: &lib.store.source, uid: uint64, key: rawstring, value: uint64): {} queries.actor_conf_int_set.exec(src,uid,key,value) end]; actor_conf_int_reset = [terra(src: &lib.store.source, uid: uint64, key: rawstring): {} queries.actor_conf_int_reset.exec(src,uid,key) end]; + + circle_search = [terra( + src: &lib.store.source, + pool:&lib.mem.pool, + uid: uint64, + cid: uint64 + ): lib.mem.ptr(lib.store.circle) + var res = queries.circle_search.exec(src, uid, cid) + if res.sz == 0 then return [lib.mem.ptr(lib.store.circle)].null() end + defer res:free() + + var rt = pool:alloc(lib.store.circle, res.sz) + for i = 0, res.sz do + var name = res:_string(i,0) + rt(i) = lib.store.circle { + name = name:pdup(pool); + cid = res:int(uint64,i,1); + owner = res:int(uint64,i,2); + memcount = res:int(uint64,i,3); + } + end + + return rt + end]; + + circle_members_fetch_cid = [terra( + src: &lib.store.source, + pool:&lib.mem.pool, + uid: uint64, + cid: uint64 + ): lib.mem.ptr(uint64) + var res = queries.circle_members_fetch_cid.exec(src,uid,cid) + if res.sz == 0 then return [lib.mem.ptr(uint64)].null() end + defer res:free() + + var rt = pool:alloc(uint64, res.sz) + for i = 0, res.sz do rt(i) = res:int(uint64,i,0) end + + return rt + end]; actor_auth_register_uid = nil; -- TODO better support non-view based auth } return b Index: mem.t ================================================================== --- mem.t +++ mem.t @@ -107,11 +107,14 @@ return self.ptr end terra t.methods.null(): t return t { ptr = nil, ct = 0 } end -- maybe should be a macro? terra t:ref() return self.ptr ~= nil end t.metamethods.__not = macro(function(self) return `not self:ref() end) - t.metamethods.__apply = macro(function(self,idx) return `self.ptr[idx] end) + t.metamethods.__apply = macro(function(self,idx) return `self.ptr[ [idx or 0] ] end) + t.metamethods.__update = macro(function(self,idx,rhs) + return quote self.ptr[idx] = rhs end end) + if not ty:isstruct() then terra t:cmp_raw(other: &ty) for i=0, self.ct do if self.ptr[i] ~= other[i] then return false end end Index: render/timeline.t ================================================================== --- render/timeline.t +++ render/timeline.t @@ -7,17 +7,25 @@ or m == modes.mutual or m == modes.circle end local terra -render_timeline(co: &lib.srv.convo, modestr: lib.mem.ref(int8)) +render_timeline(co: &lib.srv.convo, hpath: lib.mem.ptr(lib.mem.ref(int8))) + var modestr = lib.str.ref.null() + var spec = lib.str.ref.null() + if hpath.ct >= 2 then + modestr = hpath(1) + if hpath.ct >= 3 then spec = hpath(2) end + end var mode = modes.follow var circle: uint64 = 0 - if modestr:cmp('local') then mode = [modes['local']] - elseif modestr:cmp('mutual') then mode = modes.mutual - elseif modestr:cmp('fedi') then mode = modes.fedi - elseif modestr:cmp('circle') then mode = modes.circle + if modestr:ref() then + if modestr:cmp('local' ) then mode = [modes['local']] + elseif modestr:cmp('mutual') then mode = modes.mutual + elseif modestr:cmp('fedi' ) then mode = modes.fedi + elseif modestr:cmp('circle') then mode = modes.circle + end end if requires_login(mode) and co.aid == 0 then mode = [modes['local']] end var stoptime = lib.osclock.time(nil) @@ -43,41 +51,58 @@ var modelinks = arrayof(pstr, [modes.members]) acc:lpush('
showing ') for i=0, [modelabels.type.N] do if co.aid ~= 0 or not requires_login(i) then if i > 0 then acc:lpush(' ยท ') end - if i == mode then + if i == mode and not (mode == modes.circle and spec:ref()) then acc:lpush(''):ppush(modelabels[i]):lpush('') else acc:lpush(''):ppush(modelabels[i]):lpush('') end end end acc:lpush('
') - acc:lpush('
') var newest: lib.store.timepoint = 0 - for i = 0, posts.sz do - var author = co:uid2actor(posts(i).ptr.author) - if mode == modes.mutual and posts(i).ptr.author ~= co.who.id then - if not author.relationship.recip.follow() then goto skip end + if mode == modes.circle and not spec then + var circles = co.srv:circle_search(&co.srv.pool, co.who.id, 0) + acc:lpush '' + for i:intptr = 0, circles.ct do + acc:lpush '
  • ' + :ppush(circles(i).name) + :lpush '
  • ' + end + -- TODO list circles + acc:lpush '
    ' + else + acc:lpush('
    ') + for i = 0, posts.sz do + var author = co:uid2actor(posts(i).ptr.author) + if mode == modes.mutual and posts(i).ptr.author ~= co.who.id then + if not author.relationship.recip.follow() then goto skip end + end + if author.relationship.rel.mute() or + author.relationship.rel.avoid() or + author.relationship.recip.exclude() then goto skip end + lib.render.tweet(co, posts(i).ptr, &acc) + var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited) + if t > newest then newest = t end + ::skip:: posts(i):free() end - if author.relationship.rel.mute() or - author.relationship.rel.avoid() or - author.relationship.recip.exclude() then goto skip end - lib.render.tweet(co, posts(i).ptr, &acc) - var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited) - if t > newest then newest = t end - ::skip:: posts(i):free() + if posts.run > 0 then posts:free() end + acc:lpush('
    ') end - if posts.run > 0 then posts:free() end - acc:lpush('
    ') var doc = [lib.srv.convo.page] { title = 'timeline'; body = acc:finalize(); class = 'timeline'; cache = false; } - co:livepage(doc,newest) + if newest ~= 0 + then co:livepage(doc,newest) + else co:stdpage(doc) + end --doc.body:free() end return render_timeline Index: route.t ================================================================== --- route.t +++ route.t @@ -220,11 +220,11 @@ co:reroute(redirto.buf) end end terra http.timeline(co: &lib.srv.convo, mode: hpath) - lib.render.timeline(co,lib.trn(mode.ptr == nil, rstring{ptr=nil}, mode.ptr[1])) + lib.render.timeline(co,mode) end terra http.documentation(co: &lib.srv.convo, path: hpath) if path.ct == 2 then lib.render.docpage(co,path(1)) Index: store.t ================================================================== --- store.t +++ store.t @@ -238,10 +238,18 @@ isreply: bool source: &m.source -- save :: bool -> {} (defined in acl.t due to dep. hell) } + +struct m.circle { + cid: uint64 + owner: uint64 + name: lib.str.t +-- ephemera + memcount: intptr +} struct m.artifact { rid: uint64 owner: uint64 desc: str @@ -463,10 +471,18 @@ -- emoji: pstring (null to delete previous reaction, otherwise adds/changes) post_act_cancel: {&m.source, uint64} -> {} post_liked_uid: {&m.source, uint64, uint64} -> bool post_reacted_uid: {&m.source, uint64, uint64} -> bool post_act_fetch_notice: {&m.source, uint64} -> m.notice + + circle_search: {&m.source, &lib.mem.pool, uint64, uint64} -> lib.mem.ptr(m.circle) + circle_create: {&m.source, uint64, pstring} -> {} + circle_destroy: {&m.source, uint64, uint64} -> {} + circle_members_fetch_cid: {&m.source, &lib.mem.pool, uint64, uint64} -> lib.mem.ptr(uint64) + circle_members_fetch_name: {&m.source, &lib.mem.pool, uint64, pstring} -> lib.mem.ptr(uint64) + circle_members_add_uid: {&m.source, uint64, uint64} -> {} + circle_members_del_uid: {&m.source, uint64, uint64} -> {} thread_latest_arrival_calc: {&m.source, uint64} -> m.timepoint artifact_instantiate: {&m.source, lib.mem.ptr(uint8), lib.mem.ptr(int8)} -> uint64 -- instantiate an artifact in the database, either installing a new Index: str.t ================================================================== --- str.t +++ str.t @@ -30,11 +30,10 @@ maxlen = maxlen - 1 end return str end - do local strptr = (lib.mem.ptr(int8)) local strref = (lib.mem.ref(int8)) local byteptr = (lib.mem.ptr(uint8)) local function install_funcs(ty) ty.metamethods.__cast = function(from,to,e) @@ -44,10 +43,17 @@ elseif from == &int8 then return `ty {ptr = e, ct = m.sz(e)} elseif to == &int8 then return e.ptr end + end + terra ty:pdup(p: &lib.mem.pool): strptr + if not @self then return strptr.null() end + if self.ct == 0 then self.ct = m.sz(self.ptr) end + var newstr = p:alloc(int8, self.ct) + lib.mem.cpy(newstr.ptr, self.ptr, self.ct) + return newstr end terra ty:cmp(other: ty) if self.ptr == nil and other.ptr == nil then return true end if self.ptr == nil or other.ptr == nil then return false end