parsav  Check-in [ac4a630ad5]

Overview
Comment:enable profile editing
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ac4a630ad562c1ae6efc8ee1a5a3e5935f29f856e7a6c64d6677d272b93ba172
User & Date: lexi on 2020-12-30 02:44:15
Other Links: manifest | tags
Context
2020-12-31
00:15
add lots more shit check-in: d4ecea913f user: lexi tags: trunk
2020-12-30
02:44
enable profile editing check-in: ac4a630ad5 user: lexi tags: trunk
00:43
continued iteration check-in: 0324d62546 user: lexi tags: trunk
Changes

Modified backend/pgsql.t from [30de1dd276] to [d54604496b].

58
59
60
61
62
63
64


























65
66
67
68
69
70
71
...
823
824
825
826
827
828
829

830


831
832
833
834
835
836
837
838
839
840

841


842
843
844
845
846
847
848
...
934
935
936
937
938
939
940

941


942
943
944
945
946
947
948
...
949
950
951
952
953
954
955

956


957
958
959
960
961










962
963
964
965
966
967
968
			where $1::text = (a.handle || '@' || domain) or
			      $1::text = ('@' || a.handle || '@' || domain) or
				  (a.origin is null and
					  $1::text = a.handle or
					  $1::text = ('@' || a.handle))
		]];
	};



























	actor_create = {
		params = {
			rawstring, rawstring, uint64, lib.store.timepoint,
			rawstring, rawstring, lib.mem.ptr(uint8),
			rawstring, uint16, uint32
		};
................................................................................

	actor_enum = [terra(src: &lib.store.source)
		var r = queries.actor_enum.exec(src)
		if r.sz == 0 then
			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
		else defer r:free()
			var mem = lib.mem.heapa([&lib.store.actor], r.sz)

			for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end


			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
		end
	end];

	actor_enum_local = [terra(src: &lib.store.source)
		var r = queries.actor_enum_local.exec(src)
		if r.sz == 0 then
			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
		else defer r:free()
			var mem = lib.mem.heapa([&lib.store.actor], r.sz)

			for i=0,r.sz do mem.ptr[i] = row_to_actor(&r, i).ptr end


			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
		end
	end];

	actor_auth_how = [terra(
			src: &lib.store.source,
			ip: lib.store.inet,
................................................................................

	timeline_instance_fetch = [terra(src: &lib.store.source, rg: lib.store.range)
		var r = pqr { sz = 0 }
		var A,B,C,D = rg:matrix() -- :/
		r = queries.timeline_instance_fetch.exec(src,A,B,C,D)
		
		var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz)

		for i=0,r.sz do ret.ptr[i] = row_to_post(&r, i) end -- MUST FREE ALL



		return ret
	end];

	post_enum_author_uid = [terra(
		src: &lib.store.source,
		uid: uint64,
................................................................................
		rg: lib.store.range
	): lib.mem.ptr(lib.mem.ptr(lib.store.post))
		var r = pqr { sz = 0 }
		var A,B,C,D = rg:matrix() -- :/
		r = queries.post_enum_author_uid.exec(src,A,B,C,D,uid)
		
		var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz)

		for i=0,r.sz do ret.ptr[i] = row_to_post(&r, i) end -- MUST FREE ALL



		return ret
	end];

	actor_powers_fetch = getpow;










	actor_save_privs = privupdate;

	actor_create = [terra(
		src: &lib.store.source,
		ac: &lib.store.actor
	): uint64
		var r = queries.actor_create.exec(src,ac.nym, ac.handle, ac.origin, ac.knownsince, ac.bio, ac.avatar, ac.key, ac.epithet, ac.rights.rank, ac.rights.quota)







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
|
>
>










>
|
>
>







 







>
|
>
>







 







>
|
>
>





>
>
>
>
>
>
>
>
>
>







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
...
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
...
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
...
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
			where $1::text = (a.handle || '@' || domain) or
			      $1::text = ('@' || a.handle || '@' || domain) or
				  (a.origin is null and
					  $1::text = a.handle or
					  $1::text = ('@' || a.handle))
		]];
	};

	actor_save = {
		params = {
			uint64, --id
			rawstring, --nym
			rawstring, --handle
			rawstring, --bio 
			rawstring, --epithet
			rawstring, --avataruri
			uint64, --avatarid
			uint16, --rank
			uint32 --quota
		}, cmd = true, sql = [[
			update parsav_actors set
				nym = $2::text,
				handle = $3::text,
				bio = $4::text,
				epithet = $5::text,
				avataruri = $6::text,
				avatarid = $7::bigint,
				rank = $8::smallint,
				quota = $9::integer
				--invites are controlled by their own specialized routines
			where id = $1::bigint
		]];
	};

	actor_create = {
		params = {
			rawstring, rawstring, uint64, lib.store.timepoint,
			rawstring, rawstring, lib.mem.ptr(uint8),
			rawstring, uint16, uint32
		};
................................................................................

	actor_enum = [terra(src: &lib.store.source)
		var r = queries.actor_enum.exec(src)
		if r.sz == 0 then
			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
		else defer r:free()
			var mem = lib.mem.heapa([&lib.store.actor], r.sz)
			for i=0,r.sz do
				mem.ptr[i] = row_to_actor(&r, i).ptr
				mem.ptr[i].source = src
			end
			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
		end
	end];

	actor_enum_local = [terra(src: &lib.store.source)
		var r = queries.actor_enum_local.exec(src)
		if r.sz == 0 then
			return [lib.mem.ptr(&lib.store.actor)] { ct = 0, ptr = nil }
		else defer r:free()
			var mem = lib.mem.heapa([&lib.store.actor], r.sz)
			for i=0,r.sz do
				mem.ptr[i] = row_to_actor(&r, i).ptr
				mem.ptr[i].source = src
			end
			return [lib.mem.ptr(&lib.store.actor)] { ct = r.sz, ptr = mem.ptr }
		end
	end];

	actor_auth_how = [terra(
			src: &lib.store.source,
			ip: lib.store.inet,
................................................................................

	timeline_instance_fetch = [terra(src: &lib.store.source, rg: lib.store.range)
		var r = pqr { sz = 0 }
		var A,B,C,D = rg:matrix() -- :/
		r = queries.timeline_instance_fetch.exec(src,A,B,C,D)
		
		var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz)
		for i=0,r.sz do
			ret.ptr[i] = row_to_post(&r, i) -- MUST FREE ALL
			ret.ptr[i].ptr.source = src
		end

		return ret
	end];

	post_enum_author_uid = [terra(
		src: &lib.store.source,
		uid: uint64,
................................................................................
		rg: lib.store.range
	): lib.mem.ptr(lib.mem.ptr(lib.store.post))
		var r = pqr { sz = 0 }
		var A,B,C,D = rg:matrix() -- :/
		r = queries.post_enum_author_uid.exec(src,A,B,C,D,uid)
		
		var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz)
		for i=0,r.sz do
			ret.ptr[i] = row_to_post(&r, i) -- MUST FREE ALL
			ret.ptr[i].ptr.source = src
		end

		return ret
	end];

	actor_powers_fetch = getpow;
	actor_save = [terra(
		src: &lib.store.source,
		ac: &lib.store.actor
	): {}
		queries.actor_save.exec(src,
			ac.id, ac.nym, ac.handle,
			ac.bio, ac.epithet, ac.avatar,
			ac.avatarid, ac.rights.rank, ac.rights.quota)
	end];

	actor_save_privs = privupdate;

	actor_create = [terra(
		src: &lib.store.source,
		ac: &lib.store.actor
	): uint64
		var r = queries.actor_create.exec(src,ac.nym, ac.handle, ac.origin, ac.knownsince, ac.bio, ac.avatar, ac.key, ac.epithet, ac.rights.rank, ac.rights.quota)

Modified parsav.t from [b85f6f7647] to [dc97a3f269].

558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
	print(util.dump(config))
	os.exit(0)
end

local holler = print
local suffix = config.exe and '' or ('.'..config.outform)
local out = 'parsavd' .. suffix
local linkargs = {}
local target = config.tgttrip and terralib.newtarget {
	Triple = config.tgttrip;
	CPU = config.tgtcpu;
	FloatABIHard = config.tgthf;
} or nil

if bflag('quiet','q') then holler = function() end end







|







558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
	print(util.dump(config))
	os.exit(0)
end

local holler = print
local suffix = config.exe and '' or ('.'..config.outform)
local out = 'parsavd' .. suffix
local linkargs = {'-O4'}
local target = config.tgttrip and terralib.newtarget {
	Triple = config.tgttrip;
	CPU = config.tgtcpu;
	FloatABIHard = config.tgthf;
} or nil

if bflag('quiet','q') then holler = function() end end

Modified render/conf.t from [79b6da76d7] to [1b75d5dd6d].

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
..
58
59
60
61
62
63
64







65

66
67
68
69
70
71
72
73
74
75
				body:free()
			else [invoker] end
		end
	end
end

local terra 
render_conf([co], [path])
	var menu: lib.str.acc menu:init(64):lpush('<hr>') defer menu:free()

	-- build menu
	do var p = co.who.rights.powers
		if p.config() then menu:lpush '<a href="/conf/srv">server settings</a>' end
		if p.rebrand() then menu:lpush '<a href="/conf/brand">instance branding</a>' end
		if p.censor() then menu:lpush '<a href="/conf/censor">badthink alerts</a>' end
................................................................................
	var mptr = pstr { ptr = menu.buf, ct = menu.sz }
	if menu.sz <= 4 then mptr.ct = 0 end -- 🙄
	var pg = data.view.conf {
		menu = mptr;
		panel = panel;
	}








	var pgt = pg:tostr() defer pgt:free()

	co:stdpage([lib.srv.convo.page] {
		title = 'configure'; body = pgt;
		class = lib.str.plit 'conf';
		cache = false;
	})

	if panel.ct ~= 0 then panel:free() end
end

return render_conf







|







 







>
>
>
>
>
>
>
|
>










35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
..
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
				body:free()
			else [invoker] end
		end
	end
end

local terra 
render_conf([co], [path], notify: pstr)
	var menu: lib.str.acc menu:init(64):lpush('<hr>') defer menu:free()

	-- build menu
	do var p = co.who.rights.powers
		if p.config() then menu:lpush '<a href="/conf/srv">server settings</a>' end
		if p.rebrand() then menu:lpush '<a href="/conf/brand">instance branding</a>' end
		if p.censor() then menu:lpush '<a href="/conf/censor">badthink alerts</a>' end
................................................................................
	var mptr = pstr { ptr = menu.buf, ct = menu.sz }
	if menu.sz <= 4 then mptr.ct = 0 end -- 🙄
	var pg = data.view.conf {
		menu = mptr;
		panel = panel;
	}

	var pgt: pstr
	if notify:ref() then
		var fnpg: lib.str.acc
		fnpg:compose('<div class="flashmsg">', notify, '</div>')
		pg:append(&fnpg)
		pgt = fnpg:finalize()
	else pgt = pg:tostr() end
	defer pgt:free()

	co:stdpage([lib.srv.convo.page] {
		title = 'configure'; body = pgt;
		class = lib.str.plit 'conf';
		cache = false;
	})

	if panel.ct ~= 0 then panel:free() end
end

return render_conf

Modified render/profile.t from [03b39adc21] to [19457b4b7c].

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
..
50
51
52
53
54
55
56
57
58
59
60
61
62
local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local terra 
render_profile(co: &lib.srv.convo, actor: &lib.store.actor)
	var aux: lib.str.acc
	var auxp: pstr
	if co.aid ~= 0 and co.who.id == actor.id then
		auxp = lib.str.plit '<a href="/conf/profile">alter</a>'
	elseif co.aid ~= 0 then
		aux:compose('<a href="/', actor.xid, '/follow">follow</a><a href="/',
			actor.xid, '/chat">chat</a>')
		if co.who.rights.powers:affect_users() then
			aux:lpush('<a href="/'):push(actor.xid,0):lpush('/ctl">control</a>')
		end
		auxp = aux:finalize()
	else
		aux:compose('<a href="/', actor.xid, '/follow">remote follow</a>')
		auxp = aux:finalize()
	end

	var avistr: lib.str.acc if actor.origin == 0 then
		avistr:compose('/avi/',actor.handle)
	end
	var timestr: int8[26] lib.osclock.ctime_r(&actor.knownsince, &timestr[0])

	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 = lib.str.plit "<em>tall, dark, and mysterious</em>"
	if actor.bio ~= nil then
		bio = lib.html.sanitize(cs(actor.bio), false)
	end
	var fullname = lib.render.nym(actor,0) defer fullname:free()
	var profile = data.view.profile {
		nym = fullname;
		bio = bio;
		xid = cs(actor.xid);
		avatar = lib.trn(actor.origin == 0, pstr{ptr=avistr.buf,ct=avistr.sz},
................................................................................
		timephrase = lib.trn(actor.origin == 0, lib.str.plit'joined', lib.str.plit'known since');

		auxbtn = auxp;
	}

	var ret = profile:tostr()
	if actor.origin == 0 then avistr:free() end
	if not (co.aid ~= 0 and co.who.id == actor.id) then auxp:free() end
	if actor.bio ~= nil then bio:free() end
	return ret
end

return render_profile







<

|






<


<

>













|







 







|





3
4
5
6
7
8
9

10
11
12
13
14
15
16
17

18
19

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
..
48
49
50
51
52
53
54
55
56
57
58
59
60
local terra cs(s: rawstring)
	return pstr { ptr = s, ct = lib.str.sz(s) }
end

local terra 
render_profile(co: &lib.srv.convo, actor: &lib.store.actor)
	var aux: lib.str.acc

	if co.aid ~= 0 and co.who.id == actor.id then
		aux:compose('<a href="/conf/profile?go=/',actor.xid,'">alter</a>')
	elseif co.aid ~= 0 then
		aux:compose('<a href="/', actor.xid, '/follow">follow</a><a href="/',
			actor.xid, '/chat">chat</a>')
		if co.who.rights.powers:affect_users() then
			aux:lpush('<a href="/'):push(actor.xid,0):lpush('/ctl">control</a>')
		end

	else
		aux:compose('<a href="/', actor.xid, '/follow">remote follow</a>')

	end
	var auxp = aux:finalize()
	var avistr: lib.str.acc if actor.origin == 0 then
		avistr:compose('/avi/',actor.handle)
	end
	var timestr: int8[26] lib.osclock.ctime_r(&actor.knownsince, &timestr[0])

	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 = lib.str.plit "<em>tall, dark, and mysterious</em>"
	if actor.bio ~= nil then
		bio = lib.smackdown.html(cs(actor.bio))
	end
	var fullname = lib.render.nym(actor,0) defer fullname:free()
	var profile = data.view.profile {
		nym = fullname;
		bio = bio;
		xid = cs(actor.xid);
		avatar = lib.trn(actor.origin == 0, pstr{ptr=avistr.buf,ct=avistr.sz},
................................................................................
		timephrase = lib.trn(actor.origin == 0, lib.str.plit'joined', lib.str.plit'known since');

		auxbtn = auxp;
	}

	var ret = profile:tostr()
	if actor.origin == 0 then avistr:free() end
	auxp:free() 
	if actor.bio ~= nil then bio:free() end
	return ret
end

return render_profile

Modified route.t from [5e16c3f22b] to [ae96c17fe6].

172
173
174
175
176
177
178
179



























180
181
182
183
184
185
186
187
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
	elseif path.ct == 1 then
		lib.render.docpage(co, rstring.null())
	else
		co:complain(404, 'no such documentation', 'invalid documentation URL')
	end
end

terra http.configure(co: &lib.srv.convo, path: hpath)



























	lib.render.conf(co,path)
end

do local branches = quote end
	local filename, flen = symbol(&int8), symbol(intptr)
	local page = symbol(lib.http.page)
	local send = label()
	local storage = data.stmap
................................................................................
		elseif path.ptr[0]:cmp(lib.str.lit('tl')) then
			http.timeline(co, path)
		elseif path.ptr[0]:cmp(lib.str.lit('doc')) then
			if meth ~= method.get and meth ~= method.head then goto wrongmeth end
			http.documentation(co, path)
		elseif path.ptr[0]:cmp(lib.str.lit('conf')) then
			if co.aid == 0 then goto unauth end
			http.configure(co,path)
		else goto notfound end
		return
	end

	::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
	::notfound:: co:complain(404, 'not found', 'no such resource available') do return end
	::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







 







|








172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
...
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
	elseif path.ct == 1 then
		lib.render.docpage(co, rstring.null())
	else
		co:complain(404, 'no such documentation', 'invalid documentation URL')
	end
end

terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t)
	var msg = pstring.null()
	if meth == method.post and path.ct >= 1 then
		var user_refresh = false var fail = false
		if path(1):cmp(lib.str.lit 'profile') then
			co.who.bio = co:postv('bio')._0
			co.who.nym = co:postv('nym')._0
			if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end
			if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
			co.who.source:actor_save(co.who)
			msg = lib.str.plit 'profile changes saved'
			--user_refresh = true -- not really necessary here, actually
		elseif path(1):cmp(lib.str.lit 'srv') then
		elseif path(1):cmp(lib.str.lit 'users') then

		end

		if user_refresh then -- refresh the user info for the renderer
			var usr = co.srv:actor_fetch_uid(co.who.id)
			lib.mem.heapf(co.who)
			co.who = usr.ptr
		end
		var go,golen = co:getv('go')
		if not fail and go ~= nil then
			co:reroute(go)
			return
		end
	end
	lib.render.conf(co,path,msg)
end

do local branches = quote end
	local filename, flen = symbol(&int8), symbol(intptr)
	local page = symbol(lib.http.page)
	local send = label()
	local storage = data.stmap
................................................................................
		elseif path.ptr[0]:cmp(lib.str.lit('tl')) then
			http.timeline(co, path)
		elseif path.ptr[0]:cmp(lib.str.lit('doc')) then
			if meth ~= method.get and meth ~= method.head then goto wrongmeth end
			http.documentation(co, path)
		elseif path.ptr[0]:cmp(lib.str.lit('conf')) then
			if co.aid == 0 then goto unauth end
			http.configure(co,path,meth)
		else goto notfound end
		return
	end

	::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
	::notfound:: co:complain(404, 'not found', 'no such resource available') do return end
	::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end

Modified static/style.scss from [a08d589c48] to [2a06f65525].

557
558
559
560
561
562
563

564
565
566
567
568
569
570


























		.txtbox {
			@extend %serif;
			box-sizing: border-box;
			padding: 0.08in 0.1in;
			border: 1px solid black;
			background: tone(-55%);
		}

		input, textarea, .txtbox {
			display: block;
			width: 100%;
		}
		button { float: right; width: 50%; }
	}
}

































>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
		.txtbox {
			@extend %serif;
			box-sizing: border-box;
			padding: 0.08in 0.1in;
			border: 1px solid black;
			background: tone(-55%);
		}
		textarea { resize: vertical; min-height: 2in; }
		input, textarea, .txtbox {
			display: block;
			width: 100%;
		}
		button { float: right; width: 50%; }
	}
}

@keyframes flashup {
	0% { opacity: 0; transform: scale(0.8); }
	10% { opacity: 1; transform: scale(1.1); }
	80% { opacity: 1; transform: scale(1); }
	100% { opacity: 0; transform: scale(0.9) translateY(-0.12in); display: none; }
}
.flashmsg {
	display: block;
	position: fixed;
	top: 1.3in;
	max-width: 3in;
	padding: 0.5in 0.2in;
	left: 0; right: 0;
	text-align: center;
	text-shadow: 0 0 15px tone(10%);
	margin: auto;
	background: linear-gradient(to bottom, tone(-49%), tone(-43%,-0.1));
	border: 1px solid tone(0%);
	border-radius: 3px;
	box-shadow: 0 0 50px tone(-55%);
	color: white;
	animation: ease forwards flashup;
	//cubic-bezier(0.4, 0.63, 0.6, 0.31)
	animation-duration: 3s;
}

Modified store.t from [69369cc5c2] to [f53ab94a55].

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
..
79
80
81
82
83
84
85

86
87
88
89
90
91
92
...
247
248
249
250
251
252
253

254
255
256
257
258
259
260

struct m.source

struct m.rights {
	rank: uint16 -- lower = more powerful except 0 = regular user
	-- creating staff automatically assigns rank immediately below you
	quota: uint32 -- # of allowed tweets per day; 0 = no limit
	invites: intptr -- # of people left this user can invite
	
	powers: m.powerset
}

terra m.rights_default()
	var pow: m.powerset pow:clear()
	(pow.login     << true)
................................................................................
	id: uint64
	nym: str
	handle: str
	origin: uint64
	bio: str
	epithet: str
	avatar: str

	knownsince: m.timepoint
	rights: m.rights
	key: lib.mem.ptr(uint8)

-- ephemera
	xid: str
	source: &m.source
................................................................................
	-- not have the desired effect

	conf_get: {&m.source, rawstring} -> lib.mem.ptr(int8)
	conf_set: {&m.source, rawstring, rawstring} -> {}
	conf_reset: {&m.source, rawstring} -> {}

	actor_create: {&m.source, &m.actor} -> uint64

	actor_save_privs: {&m.source, &m.actor} -> {}
	actor_fetch_xid: {&m.source, lib.mem.ptr(int8)} -> lib.mem.ptr(m.actor)
	actor_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.actor)
	actor_notif_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.notif)
	actor_enum: {&m.source} -> lib.mem.ptr(&m.actor)
	actor_enum_local: {&m.source} -> lib.mem.ptr(&m.actor)
	actor_stats: {&m.source, uint64} -> m.actor_stats







|







 







>







 







>







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
..
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

struct m.source

struct m.rights {
	rank: uint16 -- lower = more powerful except 0 = regular user
	-- creating staff automatically assigns rank immediately below you
	quota: uint32 -- # of allowed tweets per day; 0 = no limit
	invites: uint32 -- # of people left this user can invite
	
	powers: m.powerset
}

terra m.rights_default()
	var pow: m.powerset pow:clear()
	(pow.login     << true)
................................................................................
	id: uint64
	nym: str
	handle: str
	origin: uint64
	bio: str
	epithet: str
	avatar: str
	avatarid: uint64
	knownsince: m.timepoint
	rights: m.rights
	key: lib.mem.ptr(uint8)

-- ephemera
	xid: str
	source: &m.source
................................................................................
	-- not have the desired effect

	conf_get: {&m.source, rawstring} -> lib.mem.ptr(int8)
	conf_set: {&m.source, rawstring, rawstring} -> {}
	conf_reset: {&m.source, rawstring} -> {}

	actor_create: {&m.source, &m.actor} -> uint64
	actor_save: {&m.source, &m.actor} -> {}
	actor_save_privs: {&m.source, &m.actor} -> {}
	actor_fetch_xid: {&m.source, lib.mem.ptr(int8)} -> lib.mem.ptr(m.actor)
	actor_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.actor)
	actor_notif_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.notif)
	actor_enum: {&m.source} -> lib.mem.ptr(&m.actor)
	actor_enum_local: {&m.source} -> lib.mem.ptr(&m.actor)
	actor_stats: {&m.source, uint64} -> m.actor_stats