parsav  profile.t at [64ae6724c2]

File render/profile.t artifact c63a8aaea6 part of check-in 64ae6724c2


-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local relkinds = {
	pos = {
		{ id = 'follow', start = {
			text = 'follow';
			desc = "this user's posts will appear in your timeline";
		}, stop = {
			text = 'unfollow';
			desc = "this user's posts will no longer appear in your timeline";
		}};

		{ id = 'sub', start = {
			text = 'subscribe';
			desc = "you will get a notification whenever this user posts";
		}, stop = {
			text = 'unsubscribe';
			desc = "you will no longer get notifications when this user posts";
		}};
	};
	neg = {
		{ id = 'mute', start = {
			text = 'mute';
			desc = "this user's posts will no longer appear anywhere";
		}, stop = {
			text = 'unmute';
			desc = "this user's posts will once again be visible to you";
		}};

		{ id = 'exclude', start = {
			text = 'exclude';
			desc = "this user will not be able to see your posts";
		}, stop = {
			text = 'reinclude';
			desc = "this user will again be able to see your posts";
		}};

		{ id = 'avoid', start = {
			text = 'avoid';
			desc = "this user's posts will not appear on your timeline even if you follow them and you will not receive notices from them, but they will still be visible in threads";
		}, stop = {
			text = 'reconcile';
			desc = "this user will once again be able to originate notices to you, and will be shown on your timeline";
		}};

		{ id = 'collapse', start = {
			text = 'collapse';
			desc = "this user's posts will still appear but will be collapsed by default, so you only see the text if you choose to expand each post";
		}, stop = {
			text = 'expand';
			desc = "this user's posts will no longer be collapsed";
		}};

		{ id = 'disemvowel', start = {
			text = 'disemvowel'; -- translations should not translate this literally
			desc = "this user's posts will be ritually mutilated in a humorous fashion as appropriate to the script in which they are written; e.g. the removal of vowels in roman text and deletion of kana in japanese text";
		}, stop = {
			text = 're-emvowel';
			desc = "this user's posts will once again appear normally";
		}};

		{ id = 'attenuate', start = {
			text = 'attenuate'; 
			desc = "this user will no longer be able to retweet things into your timeline";
		}, stop = {
			text = 'amplify';
			desc = "this user's retweets will be allowed to reach your timeline again";
		}};

		{ id = 'block', start = {
			text = 'block';
			desc = "this user will not be able to interact with you in any fashion and they will be forced to unfollow you";
		}, stop = {
			text = 'unblock';
			desc = "this user will once again be able to interact with you";
		}};
	};
}

local function
btnhtml(kind)
	local function sb(class)
		local b = kind[class]
		return string.format('<div class="opt%s">' ..
			'<button name="act" value="%s">%s</button>' ..
			'<p>%s</p>' .. 
		'</div>',
			(class == 'stop' and ' on' or ''),
			(class == 'stop' and 'un' or '') .. kind.id,
			b.text, b.desc)
	end
	return sb('start'), sb('stop')
end

local terra 
render_profile(
	co: &lib.srv.convo,
	actor: &lib.store.actor,
	relationship: &lib.store.relationship
): pstr
	var aux = co:stra(128)
	var followed = false -- FIXME
	if co.aid ~= 0 and co.who.id == actor.id then
		aux:lpush('<a accesskey="a" class="button" href="/conf/profile?go=/@'):push(actor.handle,0):lpush('">alter</a>')
	elseif co.aid ~= 0 then
		aux:lpush('<a class="button" href="#rel">options</a>')
		if co.who.rights.powers:affect_users() and co.who:overpowers(actor) then
			aux:lpush(' <a accesskey="n" class="button" href="/conf/users/'):shpush(actor.id):lpush('">control</a>')
		end
	else
		aux:lpush(' <a accesskey="f" class="button" href="/'):push(actor.xid,0):lpush('/follow">remote follow</a>')
	end
	var auxp = aux:finalize()
	var timestr: int8[26] lib.osclock.ctime_r(&actor.knownsince, &timestr[0])

	var relbtns = co:stra(256)
	var sancbtns = co:stra(256)
	[(function()
		local allkinds = {}
		for kind, rels in pairs(relkinds) do
			for i,v in ipairs(rels) do
				v.kind = kind
				allkinds[#allkinds + 1] = v
			end
		end
		local br = {}
		for i,v in ipairs(allkinds) do
			local off, on = btnhtml(v)
			local target = v.kind == 'pos' and relbtns or sancbtns
			br[#br+1] = quote
				if relationship.rel.[v.id]()
					then target:ppush(lib.str.plit([on]))
					else target:ppush(lib.str.plit([off]))
				end
			end
		end
		return br
	end)()]

	var strfbuf: int8[28*4]
	var stats = co.srv:actor_stats(actor.id)
		var sn_posts     = cs(lib.math.decstr_friendly(stats.posts, &strfbuf[ [strfbuf.type.N - 1] ]))
		var sn_follows   = cs(lib.math.decstr_friendly(stats.follows, sn_posts.ptr - 1))
		var sn_followers = cs(lib.math.decstr_friendly(stats.followers, sn_follows.ptr - 1))
		var sn_mutuals   = cs(lib.math.decstr_friendly(stats.mutuals, sn_followers.ptr - 1))
	var bio = pstr '<em style="opacity:0.6">tall, dark, and mysterious</em>'
	if actor.bio ~= nil then
		bio = lib.smackdown.html(&co.srv.pool,cs(actor.bio),false)
	end
	var fullname = lib.render.nym(actor,0,nil,false) defer fullname:free()
	var comments = co:stra(64)

	if co.srv.cfg.master == actor.id then
		var foundertxt = pstr 'founder'
		if co.srv.cfg.ui_cue_founder:ref() then
			if co.srv.cfg.ui_cue_founder.ct == 0 -- empty string, suppress field
				then foundertxt = pstr.null()
				else foundertxt = co.srv.cfg.ui_cue_founder
			end
		end

		if foundertxt:ref() then
			comments:lpush('<li style="--co:-70">'):ppush(foundertxt):lpush('</li>')
		end
	end
	if co.aid ~= 0 and actor.rights.rank ~= 0 then
		var stafftxt = pstr 'site staff'
		if co.srv.cfg.ui_cue_staff:ref() then
			if co.srv.cfg.ui_cue_staff.ct == 0 -- empty string, suppress field
				then stafftxt = pstr.null()
				else stafftxt = co.srv.cfg.ui_cue_staff
			end
		end

		-- this is really more what epithets are for, i think
		if actor.rights.rank > 0 and stafftxt:ref() then
			comments:lpush('<li>'):ppush(stafftxt):lpush('</li>')
		end

		if co.who.rights.rank ~= 0 then
			if co.who:outranks(actor) then
				comments:lpush('<li style="--co:50">underling</li>')
			elseif actor:outranks(co.who) then
				comments:lpush('<li style="--co:-50">outranks you</li>')
			end
		end
	end

	if relationship.rel.block() then
		comments:lpush('<li style="--co:80">blocked</li>')
	end

	if relationship.recip.follow() then
		comments:lpush('<li style="--co:30">follows you</li>')
	end

	var circpanel: lib.str.acc
	if co.aid ~= 0 then
		circpanel = co:stra(128)
		var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
		if allcircs:ref() then
			var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
			for i=0, allcircs.ct do
				circpanel:lpush '<label><input type="checkbox" name="circle" value="'
						 :shpush(allcircs(i).cid)
						 :lpush '"'
				for j=0, mycircs.ct do
					if allcircs(i).cid == mycircs(j).cid then
						circpanel:lpush ' checked'
						break
					end
				end
				circpanel:lpush '> '
						 :ppush(allcircs(i).name)
						 :lpush '</label>'
			end
		end
	end

	var profile = data.view.profile {
		nym = fullname;
		bio = bio;
		xid = cs(actor.xid);
		avatar = cs(actor.avatar);

		nposts = sn_posts, nfollows = sn_follows;
		nfollowers = sn_followers, nmutuals = sn_mutuals;
		tweetday = cs(timestr);
		timephrase = lib.trn(actor.origin == 0, pstr 'joined', pstr 'known since');

		remarks = '';

		auxbtn = auxp;
		circles = circpanel:finalize();
		relations = relbtns:finalize();
		sanctions = sancbtns:finalize();
	}
	if comments.sz > 0 then profile.remarks = comments:finalize() end

	var ret = profile:poolstr(&co.srv.pool)
	-- auxp:free() 
	--if actor.bio ~= nil then bio:free() end
	--if comments.sz > 0 then profile.remarks:free() end
	return ret
end

return render_profile