parsav  Check-in [c43a1b6e9d]

Overview
Comment:circle management complete
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c43a1b6e9d9566740bfc298e737c07b3591f8689fbc4119c68b72a192c661e96
User & Date: lexi on 2021-01-14 17:37:19
Other Links: manifest | tags
Context
2021-01-14
17:38
check in missing file check-in: 576487f566 user: lexi tags: trunk
17:37
circle management complete check-in: c43a1b6e9d user: lexi tags: trunk
05:31
more progress on ui & circles check-in: e78334fe04 user: lexi tags: trunk
Changes

Modified backend/pgsql.t from [dc07991d31] to [4517a951a0].

   329    329   	circle_members_fetch_name = {
   330    330   		params = {uint64, pstring}, sql = [[
   331    331   			select unnest(members) from parsav_circles where
   332    332   				($1::bigint = 0 or owner = $1::bigint) and
   333    333   				name = $2::text
   334    334   		]];
   335    335   	};
          336  +
          337  +	circle_members_add_uid = {
          338  +		params = {uint64, uint64}, cmd = true, sql = [[
          339  +			update parsav_circles set
          340  +				members = members || $2::bigint
          341  +			where id = $1::bigint
          342  +		]];
          343  +	};
          344  +
          345  +	circle_members_del_uid = {
          346  +		params = {uint64, uint64}, cmd = true, sql = [[
          347  +			update parsav_circles set
          348  +				members = array_remove(members, $2::bigint)
          349  +			where id = $1::bigint
          350  +		]];
          351  +	};
   336    352   
   337    353   	auth_sigtime_user_fetch = {
   338    354   		params = {uint64}, sql = [[
   339    355   			select authtime::bigint
   340    356   			from parsav_actors where id = $1::bigint
   341    357   		]];
   342    358   	};
................................................................................
  2202   2218   
  2203   2219   		var rt = pool:alloc(uint64, res.sz)
  2204   2220   		for i = 0, res.sz do rt(i) = res:int(uint64,i,0) end
  2205   2221   
  2206   2222   		return rt
  2207   2223   	end];
  2208   2224   
         2225  +	circle_members_add_uid = [terra(
         2226  +		src: &lib.store.source, owner: uint64, subject: uint64
         2227  +	): {} queries.circle_members_add_uid.exec(src,owner,subject) end];
         2228  +
         2229  +	circle_members_del_uid = [terra(
         2230  +		src: &lib.store.source, owner: uint64, subject: uint64
         2231  +	): {} queries.circle_members_del_uid.exec(src,owner,subject) end];
         2232  +
  2209   2233   	actor_auth_register_uid = nil; -- TODO better support non-view based auth
  2210   2234   }
  2211   2235   
  2212   2236   return b

Modified config.lua from [e037d77845] to [af9c45003f].

    55     55   		{'live.js', 'text/javascript'}; -- rrrrrrrr
    56     56   		{'default-avatar.webp', 'image/webp'}; -- needs inkscape-exclusive svg features
    57     57   		{'bell.svg', 'image/svg+xml'};
    58     58   		{'gear.svg', 'image/svg+xml'};
    59     59   		{'pen.svg', 'image/svg+xml'};
    60     60   		{'heart.webp', 'image/webp'};
    61     61   		{'retweet.webp', 'image/webp'};
    62         -		{'logo.svg', 'image/svg+xml'};
           62  +		{'icon.svg', 'image/svg+xml'};
    63     63   		{'padlock.svg', 'image/svg+xml'};
    64     64   		{'warn.svg', 'image/svg+xml'};
    65     65   		{'query.webp', 'image/webp'};
    66     66   		{'reply.webp', 'image/webp'};
    67     67   		{'file.webp', 'image/webp'};
    68     68   		{'follow.webp', 'image/webp'};
    69     69   		-- keep in mind before you add anything to this list: these are not

Modified render/conf/circles.t from [2450dbe8a2] to [48e04956e4].

     6      6   local terra 
     7      7   render_conf_circles(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
     8      8   	if path.ct == 2 then
     9      9   		var circs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
    10     10   		var ui: data.view.conf_circles
    11     11   		if circs.ct == 0 then
    12     12   			ui.newattr = ' open';
           13  +			ui.circles = '';
    13     14   		else
    14     15   			ui.newattr = '';
    15     16   			var circlst = co:stra(86)
    16     17   			for i = 0, circs.ct do
    17     18   				circlst:lpush '<li><a href="/conf/circles/':shpush(circs(i).cid):lpush'">'
    18     19   					   :ppush(circs(i).name)
    19     20   					   :lpush '</a></li>'

Modified render/nav.t from [665c264b07] to [079076f046].

     2      2   local terra 
     3      3   render_nav(co: &lib.srv.convo)
     4      4   	var t = co:stra(64)
     5      5   	if co.who ~= nil or co.srv.cfg.pol_sec == lib.srv.secmode.public then
     6      6   		t:lpush(' <a accesskey="t" href="/"><u>t</u>imeline</a>')
     7      7   	end
     8      8   	if co.who ~= nil then
     9         -		t:lpush(' <a accesskey="m" href="/media"><u>m</u>edia</a> <a accesskey="d" href="/doc"><u>d</u>ocs</a> <hr> <a accesskey="p" href="'):push(co.who.xid,0):lpush('" class="ident">')
            9  +		t:lpush(' <a accesskey="m" href="/media"><u>m</u>edia</a> <a accesskey="d" href="/doc"><u>d</u>ocs</a> <hr> <a accesskey="p" href="/'):push(co.who.xid,0):lpush('" class="ident">')
    10     10   		t:push(co.who.handle,0)
    11     11   		t:lpush('</a> <a accesskey="g" href="/logout">lo<u>g</u> out</a> <a class="pen" accesskey="c" href="/compose"><u>c</u>ompose</a> <a class="gear" accesskey="o" href="/conf">c<u>o</u>nfigure</a> <a class="bell" accesskey="x" href="/notices">notices (<u>x</u>)</a>')
    12     12   	else
    13     13   		t:lpush(' <a accesskey="d" href="/doc"><u>d</u>ocs</a> <a accesskey="g" href="/login">lo<u>g</u> in</a>')
    14     14   	end
    15     15   	return t:finalize()
    16     16   end
    17     17   return render_nav

Modified render/profile.t from [e47888d2bc] to [c63a8aaea6].

   194    194   		comments:lpush('<li style="--co:80">blocked</li>')
   195    195   	end
   196    196   
   197    197   	if relationship.recip.follow() then
   198    198   		comments:lpush('<li style="--co:30">follows you</li>')
   199    199   	end
   200    200   
   201         -	var circpanel = co:stra(128)
   202         -	var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
   203         -	if allcircs:ref() then
   204         -		var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
   205         -		for i=0, allcircs.ct do
   206         -			circpanel:lpush '<label><input type="checkbox" name="circle" value="'
   207         -			         :shpush(allcircs(i).cid)
   208         -					 :lpush '"> '
   209         -					 :ppush(allcircs(i).name)
   210         -					 :lpush '</label>'
          201  +	var circpanel: lib.str.acc
          202  +	if co.aid ~= 0 then
          203  +		circpanel = co:stra(128)
          204  +		var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
          205  +		if allcircs:ref() then
          206  +			var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
          207  +			for i=0, allcircs.ct do
          208  +				circpanel:lpush '<label><input type="checkbox" name="circle" value="'
          209  +						 :shpush(allcircs(i).cid)
          210  +						 :lpush '"'
          211  +				for j=0, mycircs.ct do
          212  +					if allcircs(i).cid == mycircs(j).cid then
          213  +						circpanel:lpush ' checked'
          214  +						break
          215  +					end
          216  +				end
          217  +				circpanel:lpush '> '
          218  +						 :ppush(allcircs(i).name)
          219  +						 :lpush '</label>'
          220  +			end
   211    221   		end
   212    222   	end
   213    223   
   214    224   	var profile = data.view.profile {
   215    225   		nym = fullname;
   216    226   		bio = bio;
   217    227   		xid = cs(actor.xid);

Modified route.t from [23df5e6037] to [48c04bb947].

    18     18   			if rel.recip.block() then
    19     19   				if act:cmp('follow') or act:cmp('subscribe') then
    20     20   					co:complain(403,'blocked','you cannot follow a user you are blocked by') return
    21     21   				end
    22     22   			end
    23     23   			if act:cmp('circle') then
    24     24   				lib.dbg('encircling user!')
           25  +				var allcircs = co.srv:circle_search(&co.srv.pool, co.who.id, 0)
           26  +				var mycircs = co.srv:circle_memberships_uid(&co.srv.pool, co.who.id, actor.id)
           27  +				var marked = co.srv.pool:alloc(bool, allcircs.ct)
           28  +				var member = co.srv.pool:alloc(bool, allcircs.ct)
           29  +				for i = 0, marked.ct do
           30  +					marked(i) = false
           31  +					member(i) = false
           32  +					-- search through list of memberships to see if this circle is in them
           33  +					-- if it is, set its membership flag
           34  +					for j = 0, mycircs.ct do
           35  +						if mycircs(j).cid == allcircs(i).cid then
           36  +							member(i) = true
           37  +						end
           38  +					end
           39  +				end
           40  +
           41  +				-- which circles did the user mark this time?
    25     42   				var iter = co:eachpostv('circle')
    26     43   				for cid in iter do
           44  +					var decode, ok = lib.math.shorthand.parse(cid.ptr, cid.ct)
           45  +					if ok then
           46  +						for i=0, allcircs.ct do
           47  +							if allcircs(i).cid == decode then
           48  +								marked(i) = true
           49  +							end
           50  +						end
           51  +					end
           52  +				end
    27     53   
           54  +				-- compute the differential and carry out the appropriate action
           55  +				for i = 0, marked.ct do
           56  +					if marked(i) ~= member(i) then
           57  +						if marked(i) then -- newly marked, add
           58  +							co.srv:circle_members_add_uid(allcircs(i).cid, actor.id)
           59  +						elseif member(i) then -- unmarked, remove
           60  +							co.srv:circle_members_del_uid(allcircs(i).cid, actor.id)
           61  +						end
           62  +					end
    28     63   				end
           64  +
           65  +				-- thanks html for making this all so complicated
    29     66   			elseif act:cmp('block') and not rel.rel.block() then
    30     67   				rel.rel.block = true  rel.recip.follow = false
    31     68   				co.srv:actor_rel_create([lib.store.relation.idvmap.block], co.who.id, actor.id)
    32     69   				co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], actor.id, co.who.id)
    33     70   			elseif not act:cmp('report') then
    34     71   				[(function()
    35     72   					local tests = quote co:complain(400,'bad request','the action you have attempted on this user is not meaningful') return end
................................................................................
    49     86   			end
    50     87   		end
    51     88   	else
    52     89   		rel.rel:clear()
    53     90   		rel.recip:clear()
    54     91   	end
    55     92   
    56         -	lib.render.user_page(co, actor, &rel)
           93  +	var go = co:pgetv('go')
           94  +	if not go or go(0) ~= @'/' then
           95  +		lib.render.user_page(co, actor, &rel)
           96  +	else
           97  +		co:reroute(go.ptr)
           98  +	end
    57     99   end
    58    100   
    59    101   terra http.actor_profile_xid(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
    60    102   	var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 }
    61    103   	for i=2,uri.ct do
    62    104   		if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end
    63    105   	end

Added static/icon.svg version [1ac35adbf2].

            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="1in"
           14  +   height="1in"
           15  +   viewBox="0 0 25.4 25.400001"
           16  +   version="1.1"
           17  +   id="svg8"
           18  +   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
           19  +   sodipodi:docname="icon.svg"
           20  +   enable-background="new">
           21  +  <defs
           22  +     id="defs2">
           23  +    <linearGradient
           24  +       inkscape:collect="always"
           25  +       id="linearGradient937">
           26  +      <stop
           27  +         style="stop-color:#20030d;stop-opacity:0.51304346"
           28  +         offset="0"
           29  +         id="stop933" />
           30  +      <stop
           31  +         style="stop-color:#20030d;stop-opacity:0.86956519"
           32  +         offset="1"
           33  +         id="stop935" />
           34  +    </linearGradient>
           35  +    <linearGradient
           36  +       id="linearGradient2322"
           37  +       inkscape:collect="always">
           38  +      <stop
           39  +         id="stop2318"
           40  +         offset="0"
           41  +         style="stop-color:#ffe0f1;stop-opacity:1;" />
           42  +      <stop
           43  +         id="stop2320"
           44  +         offset="1"
           45  +         style="stop-color:#ff3aa6;stop-opacity:0" />
           46  +    </linearGradient>
           47  +    <linearGradient
           48  +       inkscape:collect="always"
           49  +       id="linearGradient874">
           50  +      <stop
           51  +         style="stop-color:#ffe0f1;stop-opacity:1;"
           52  +         offset="0"
           53  +         id="stop870" />
           54  +      <stop
           55  +         style="stop-color:#ff37a5;stop-opacity:0"
           56  +         offset="1"
           57  +         id="stop872" />
           58  +    </linearGradient>
           59  +    <linearGradient
           60  +       inkscape:collect="always"
           61  +       xlink:href="#linearGradient874"
           62  +       id="linearGradient876"
           63  +       x1="1.7326912"
           64  +       y1="289.11765"
           65  +       x2="22.522418"
           66  +       y2="289.11765"
           67  +       gradientUnits="userSpaceOnUse" />
           68  +    <linearGradient
           69  +       inkscape:collect="always"
           70  +       xlink:href="#linearGradient2322"
           71  +       id="linearGradient884"
           72  +       x1="23.480221"
           73  +       y1="284.29999"
           74  +       x2="4.2095594"
           75  +       y2="284.29999"
           76  +       gradientUnits="userSpaceOnUse" />
           77  +    <linearGradient
           78  +       inkscape:collect="always"
           79  +       id="linearGradient1775">
           80  +      <stop
           81  +         style="stop-color:#fef5f8;stop-opacity:1"
           82  +         offset="0"
           83  +         id="stop1771" />
           84  +      <stop
           85  +         style="stop-color:#ea2b71;stop-opacity:0"
           86  +         offset="1"
           87  +         id="stop1773" />
           88  +    </linearGradient>
           89  +    <radialGradient
           90  +       inkscape:collect="always"
           91  +       xlink:href="#linearGradient1775"
           92  +       id="radialGradient2326"
           93  +       gradientUnits="userSpaceOnUse"
           94  +       gradientTransform="matrix(1.3917117,0,0,1.3917117,-482.23699,-475.20189)"
           95  +       cx="203.03345"
           96  +       cy="185.0731"
           97  +       fx="203.03345"
           98  +       fy="185.0731"
           99  +       r="7.9787183" />
          100  +    <radialGradient
          101  +       inkscape:collect="always"
          102  +       xlink:href="#linearGradient937"
          103  +       id="radialGradient939"
          104  +       cx="204.05836"
          105  +       cy="186.09785"
          106  +       fx="204.05836"
          107  +       fy="186.09785"
          108  +       r="1.6704345"
          109  +       gradientUnits="userSpaceOnUse"
          110  +       gradientTransform="matrix(6.6930296,0,0,6.6930296,-1155.7581,-1053.5082)" />
          111  +  </defs>
          112  +  <sodipodi:namedview
          113  +     id="base"
          114  +     pagecolor="#270010"
          115  +     bordercolor="#666666"
          116  +     borderopacity="1.0"
          117  +     inkscape:pageopacity="0.30588235"
          118  +     inkscape:pageshadow="2"
          119  +     inkscape:zoom="1.4"
          120  +     inkscape:cx="221.32923"
          121  +     inkscape:cy="219.86917"
          122  +     inkscape:document-units="mm"
          123  +     inkscape:current-layer="layer1"
          124  +     showgrid="false"
          125  +     units="in"
          126  +     inkscape:object-paths="false"
          127  +     inkscape:snap-intersection-paths="true"
          128  +     inkscape:window-width="1920"
          129  +     inkscape:window-height="1042"
          130  +     inkscape:window-x="0"
          131  +     inkscape:window-y="38"
          132  +     inkscape:window-maximized="0"
          133  +     inkscape:snap-global="true"
          134  +     inkscape:snap-midpoints="true" />
          135  +  <metadata
          136  +     id="metadata5">
          137  +    <rdf:RDF>
          138  +      <cc:Work
          139  +         rdf:about="">
          140  +        <dc:format>image/svg+xml</dc:format>
          141  +        <dc:type
          142  +           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
          143  +        <dc:title></dc:title>
          144  +      </cc:Work>
          145  +    </rdf:RDF>
          146  +  </metadata>
          147  +  <g
          148  +     inkscape:label="Layer 1"
          149  +     inkscape:groupmode="layer"
          150  +     id="layer1"
          151  +     transform="translate(0,-271.59998)">
          152  +    <rect
          153  +       style="opacity:1;vector-effect:none;fill:url(#radialGradient939);fill-opacity:1;stroke:none;stroke-width:0.40000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
          154  +       id="rect931"
          155  +       width="15.245533"
          156  +       height="15.245533"
          157  +       x="202.38794"
          158  +       y="184.42741"
          159  +       transform="rotate(45)" />
          160  +    <path
          161  +       sodipodi:nodetypes="cccccccc"
          162  +       inkscape:connector-curvature="0"
          163  +       d="m 12.7,292.79042 v 0 l 8.490438,-8.49044 1.144891,1.14489 L 12.7,295.0802 1.9197799,284.29998 l 1.144891,-1.14489 z"
          164  +       style="fill:url(#linearGradient876);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
          165  +       id="path866" />
          166  +    <path
          167  +       id="path817"
          168  +       style="opacity:1;vector-effect:none;fill:none;fill-opacity:0.22173911;stroke:#ffffff;stroke-width:1.66499996;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
          169  +       d="m 12.7,279.40549 v 9.78896 m -2e-6,-9.78898 4.89448,4.89449 -4.89448,4.89447 -4.8944786,-4.89447 z"
          170  +       inkscape:connector-curvature="0" />
          171  +    <path
          172  +       id="path863"
          173  +       style="fill:url(#linearGradient884);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
          174  +       d="m 12.7,275.80954 v 0 l -8.4904379,8.49044 -1.144891,-1.14489 9.6353289,-9.63533 10.78022,10.78022 -1.144891,1.14489 z"
          175  +       inkscape:connector-curvature="0"
          176  +       sodipodi:nodetypes="cccccccc" />
          177  +  </g>
          178  +</svg>

Modified static/style.scss from [cdc5fd8fbb] to [cb730b53e2].

    89     89   	--icon: url(/s/heart.webp);
    90     90   	background-image: linear-gradient(to bottom,
    91     91   		otone(-41%),
    92     92   		otone(-43%) 15%,
    93     93   		otone(-46%) 75%,
    94     94   		otone(-50%)
    95     95   	);
           96  +	background-repeat: no-repeat; // necessary to keep gradients from glitching out under failfucks
    96     97   	&:hover, &:focus {
    97     98   		@extend %glow;
    98     99   		outline: none;
    99    100   		color: tone(-55%);
   100    101   		text-shadow: none;
   101    102   		background: linear-gradient(to bottom,
   102    103   			otone(-27%),
................................................................................
   259    260   			}
   260    261   			> a[href].bell { content: url(/s/bell.svg); }
   261    262   			> a[href].gear { content: url(/s/gear.svg); }
   262    263   			> a[href].pen { content: url(/s/pen.svg); }
   263    264   		}
   264    265   	}
   265    266   }
          267  +
   266    268   
   267    269   main {
   268    270   	@extend %content;
   269    271   	display: block;
   270    272   	position: relative;
   271    273   	min-height: calc(100vh - 1.1in);
   272    274   	margin-top: 0;
................................................................................
   292    294   		font-size: 80%;
   293    295   		vertical-align: text-top;
   294    296   	}
   295    297   }
   296    298   
   297    299   body.profile {
   298    300   	#rel {
          301  +		.check-panel { margin-top: 0.6em; }
          302  +		.check-panel + menu { margin-bottom: 2em; }
   299    303   		menu {
   300    304   			display: grid;
   301    305   			grid-template-columns: 1fr 1fr;
   302    306   			grid-template-rows: repeat(max-content);
   303    307   			grid-gap: 0.1in;
   304    308   			> .opt {
   305    309   				padding: 0.1in;
   306    310   				border-radius: 5px;
   307    311   				border: 1px solid transparent;
   308    312   				&.on {
   309         -					background-color: tone(-30%, -0.7);
   310         -					border-color: tone(-20%) transparent;
          313  +					background: linear-gradient(to bottom, tone(-30%, -0.7), transparent 80%);
          314  +					background-repeat: no-repeat;
          315  +					border-top-color: tone(-30%);
   311    316   				}
   312    317   				> button, > p, > a[href] { display: block; }
   313    318   				> p { text-align: center; font-size: 80%; margin: 0; margin-top: 0.1in; }
   314    319   				> button, > a[href] {
   315    320   					width: max-content;
   316    321   					margin: auto;
   317    322   				}
................................................................................
  1265   1270   			}
  1266   1271   			&:hover {
  1267   1272   				background-position: 0 0;
  1268   1273   			}
  1269   1274   		}
  1270   1275   	}
  1271   1276   }
         1277  +
         1278  +.modal .check-panel {
         1279  +	> label { margin: 0.1em; } 
         1280  +}

Modified view/docskel.tpl from [f103ec4227] to [5468ea7adc].

     1      1   <!doctype html>
     2      2   <html>
     3      3   	<head>
     4      4   		<title>@instance :: @title</title>
     5      5   		<link rel="stylesheet" type="text/css" href="/s/style.css">
     6         -		<link rel="icon" href="/s/logo.svg">
            6  +		<link rel="icon" href="/s/icon.svg">
     7      7   		<script type="text/javascript" src="/s/live.js" async></script>
     8      8   	</head>
     9      9   	<body class="@class"@attr>
    10     10   		<header><div>
    11     11   			<h1>@title</h1>
    12     12   			<nav>
    13     13   				<a accesskey="i" href="/instance"><u>i</u>nstance</a>

Modified view/profile.tpl from [9ae0c0e533] to [8a477a8698].

    32     32   
    33     33   <form id="rel" class="modal" method="post">
    34     34   <a href="#0" class="button">close</a><div>
    35     35   	<menu>@relations</menu>
    36     36   	<div class="check-panel">
    37     37   		@circles
    38     38   	</div>
    39         -	<button name="act" value="circle">set circles</button>
           39  +	<menu class="horizontal choice">
           40  +		<button name="act" value="circle" formaction="#">set circles and close</button>
           41  +		<button name="act" value="circle" formaction="?go=/conf/circles">set and go to circles</button>
           42  +	</menu>
    40     43   	<details>
    41     44   		<summary>sanctions</summary>
    42     45   		<menu>
    43     46   			@sanctions
    44     47   			<div class="opt">
    45     48   				<a class="neg button" href="/@:xid/report">report</a>
    46     49   				<p>if this user is violating instance rules, you can report this behavior to moderation staff and ask them to take action. please do not report users simply because you dislike them; this is what the above options are for.</p>
    47     50   			</div>
    48     51   		</menu>
    49     52   	</details>
    50     53   </div></form>