Overview
| Comment: | add like + retweets buttons, keyboard nav |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
b9cf14c14b7e9cacecd6ccdebeca2cd7 |
| User & Date: | lexi on 2021-01-04 15:29:40 |
| Other Links: | manifest | tags |
Context
|
2021-01-04
| ||
| 20:33 | more jabbascript improvements check-in: b6c2a79945 user: lexi tags: trunk | |
| 15:29 | add like + retweets buttons, keyboard nav check-in: b9cf14c14b user: lexi tags: trunk | |
| 06:44 | add likes, retweets, and iterate on a whole bunch of other shit check-in: 78b0198f09 user: lexi tags: trunk | |
Changes
Modified static/live.js from [682908b4c8] to [9100f46c08].
3 3 * interactivity beyond what native HTML+CSS can provide. if so, 4 4 * we attach the appropriate listeners to them. */ 5 5 window.addEventListener('load', function() { 6 6 /* social media is less fun when you can't just click on a tweet 7 7 * to insta-like or -retweet it. this is unfortunately not possible 8 8 * (except in various hideously shitty ways) without javascript. */ 9 9 function mk(elt) { return document.createElement(elt); } 10 + function posturl(post) { 11 + return post.querySelector('.permalink').attributes.getNamedItem('href').value; 12 + } 13 + function postReq(url,act,elt) { 14 + fetch(new Request(url, { 15 + method: 'POST', 16 + body: 'act='+act 17 + })).then(function(resp) { 18 + if (resp.ok && resp.status == 200) { 19 + var i = parseInt(elt.innerHTML) 20 + if (isNaN(i)) {i=0} 21 + elt.innerHTML = (i+1).toString() 22 + } 23 + }) 24 + } 25 + 26 + /* div-based like and rt aren't very keyboard-friendly. add a replacement */ 27 + if (document.querySelector('body.timeline, body.profile') != null) { 28 + window.addEventListener('keydown', function(event) { 29 + if (!window._liveTweetMap) { return; } 30 + if (event.isComposing || event.keyCode === 229) { return; } // 🙄 31 + var cururl = window._liveTweetMap.cur; 32 + var nexturl = null; 33 + if (event.key == 'j') { // down 34 + if (cururl == null) { 35 + nexturl = window._liveTweetMap.first 36 + } else { 37 + nexturl = window._liveTweetMap.map.get(cururl).next 38 + } 39 + } else if (event.key == 'k') { // up 40 + if (cururl == null) { 41 + nexturl = window._liveTweetMap.last 42 + } else { 43 + nexturl = window._liveTweetMap.map.get(cururl).prev 44 + } 45 + } else if (cururl != null) { 46 + var post = window._liveTweetMap.map.get(cururl).me 47 + if (event.key == 'f') { // fave 48 + postReq(cururl, 'like', post.querySelector('.stats>.like')) 49 + } else if (event.key == 'r') { // rt 50 + postReq(cururl, 'rt', post.querySelector('.stats>.rt')) 51 + } else if (event.key == 'Enter') { // nav 52 + window.location = cururl; 53 + return; 54 + } 55 + } 56 + if (nexturl != null) { 57 + if (cururl != null) { 58 + var cur = window._liveTweetMap.map.get(cururl); 59 + cur.me.classList.remove('live-selected') 60 + } 61 + var next = window._liveTweetMap.map.get(nexturl); 62 + next.me.classList.add('live-selected') 63 + window._liveTweetMap.cur = nexturl 64 + } 65 + }); 66 + } 67 + 10 68 function attachButtons() { 11 - document.querySelectorAll('body:not(.post) main div.post').forEach(function(post){ 12 - let url = post.querySelector('.permalink').attributes.getNamedItem('href').value; 13 - function postReq(act,elt) { 14 - fetch(new Request(url, { 15 - method: 'POST', 16 - body: 'act='+act 17 - })).then(function(resp) { 18 - if (resp.ok && resp.status == 200) { 19 - var i = parseInt(elt.innerHTML) 20 - if (isNaN(i)) {i=0} 21 - elt.innerHTML = (i+1).toString() 22 - } 23 - }) 69 + var last = null; 70 + var newmap = { cur: null, first: null, last: null, map: new Map() } 71 + document.querySelectorAll('body:not(.post) main article.post').forEach(function(post){ 72 + let url = posturl(post); 73 + if (last == null) { newmap.first = url; } else { 74 + newmap.map.get(last).next = url 75 + } 76 + newmap.map.set(url, {me: post, prev: last, next: null}) 77 + last = url 78 + if (window._liveTweetMap && window._liveTweetMap.cur == url) { 79 + post.classList.add('live-selected'); 24 80 } 25 81 26 82 var stats = post.querySelector('.stats'); 27 83 if (stats == null) { 28 84 /* no stats box; create one */ 29 85 var n = mk('div'); 30 86 n.classList.add('stats'); ................................................................................ 41 97 } 42 98 return n 43 99 } else { return s } 44 100 } 45 101 var like = ensureElt('like', null); 46 102 var rt = ensureElt('rt','.like'); 47 103 function activate(elt,name) { 48 - elt.addEventListener('click', function(e) { postReq(name,elt) }); 104 + elt.addEventListener('click', function(e) { postReq(url,name,elt) }); 49 105 elt.style.setProperty('cursor','pointer'); 106 + elt.setAttribute('tabindex','0'); 50 107 } 51 108 activate(like,'like'); 52 109 activate(rt,'rt'); 53 110 }); 111 + newmap.last = last 112 + if (window._liveTweetMap) { 113 + newmap.cur = window._liveTweetMap.cur // TODO handle vanishments 114 + } 115 + window._liveTweetMap = newmap 54 116 } 55 117 56 118 /* update hue-picker background when slider is adjusted */ 57 119 document.querySelectorAll('.color-picker').forEach(function(box) { 58 120 let slider = box.querySelector('[data-color-pick]'); 59 121 box.style.setProperty('--hue', slider.value); 60 122 slider.addEventListener('input', function(e) {
Modified static/style.scss from [322d30fa17] to [ea5ab728f6].
480 480 border-radius: 2px; 481 481 vertical-align: baseline; 482 482 box-shadow: 1px 1px 1px black; 483 483 } 484 484 485 485 div.thread { 486 486 margin-left: 0.3in; 487 - & + div.post { margin-top: 0.3in; } 487 + & + article.post { margin-top: 0.3in; } 488 488 } 489 489 490 490 a[href].username { 491 491 >.nym { font-weight: bold; } 492 492 color: tone(0%,-0.4); 493 493 > span.nym { color: tone(10%) } 494 494 > span.handle { color: tone(-5%) } 495 495 &:hover { 496 496 > span.nym { color: white; } 497 497 > span.handle { color: tone(15%) } 498 498 } 499 499 } 500 -div.post { 500 +article.post { 501 501 @extend %box; 502 502 display: grid; 503 503 margin: unset; 504 504 grid-template-columns: 1in 1fr max-content max-content; 505 505 grid-template-rows: min-content max-content; 506 506 margin-bottom: 0.1in; 507 + transition: 0.3s; 507 508 >.avatar { 508 509 grid-column: 1/2; grid-row: 1/2; 509 510 img { display: block; width: 1in; height: 1in; margin:0; } 510 511 background: linear-gradient(to bottom, tone(-53%), tone(-57%)); 511 512 } 512 513 >a[href].username { 513 514 display: block; ................................................................................ 542 543 grid-column: 3/4; grid-row: 2/3; 543 544 justify-content: center; 544 545 > .like, > .rt { 545 546 margin: 0.5em 0.3em; 546 547 padding-left: 1.3em; 547 548 background-size: 1.1em; 548 549 background-repeat: no-repeat; 550 + pointer-events: all; 549 551 min-width: 0.3em; 550 552 &:empty { 551 553 transition: 0.3s; 552 - opacity: 0.1; 553 - &:hover { opacity: 0.6 !important; } 554 + opacity: 0.0001; // qutebrowser won't show hints if opacity=0 :( 555 + &:hover, &:focus { opacity: 0.6 !important; } 554 556 } 555 557 } 556 - > .like { 557 - background-image: url(/s/heart.webp); 558 - } 559 - > .rt { 560 - background-image: url(/s/retweet.webp); 561 - } 558 + > .like { background-image: url(/s/heart.webp); } 559 + > .rt { background-image: url(/s/retweet.webp); } 560 + } 561 + 562 + // used for keyboard navigation 563 + &.live-selected { 564 + margin-left: 0.4in; 565 + margin-right: -0.4in; 566 + box-shadow: 0 0 0 1px tone(15%), 0 0 1in tone(5%, -0.5); 562 567 } 563 568 } 564 569 565 -div.post:hover div.stats { > .like, > .rt { &:empty {opacity: 0.3;} } } 570 +article.post:hover div.stats { > .like, > .rt { &:empty {opacity: 0.3;} } } 566 571 567 572 a[href].rawlink { 568 573 @extend %teletype; 569 574 } 570 575 571 576 body.doc main { 572 577 @extend %serif;
Modified view/tweet.tpl from [aefe28dd4e] to [80bcf01f8a].
1 -<div class="post"@attr> 1 +<article class="post"@attr> 2 2 <div class="avatar"><img src="@:avatar"></div> 3 3 <a class="username" href="/@:acctlink">@nym</a> 4 4 <div class="content"> 5 5 <div class="subject">@!subject</div> 6 6 <div class="text">@text</div> 7 7 </div> 8 8 @stats 9 - <a class="permalink" href="@permalink">@when</a> 10 -</div> 9 + <a class="permalink" href="@permalink"><time>@when</time></a> 10 +</article>