Add rendering of Spaces
This commit is contained in:
parent
a12dcae4e4
commit
2078eb8026
@ -45,6 +45,7 @@ type TweetCollection interface {
|
|||||||
Tweet(id scraper.TweetID) scraper.Tweet
|
Tweet(id scraper.TweetID) scraper.Tweet
|
||||||
User(id scraper.UserID) scraper.User
|
User(id scraper.UserID) scraper.User
|
||||||
Retweet(id scraper.TweetID) scraper.Retweet
|
Retweet(id scraper.TweetID) scraper.Retweet
|
||||||
|
Space(id scraper.SpaceID) scraper.Space
|
||||||
FocusedTweetID() scraper.TweetID
|
FocusedTweetID() scraper.TweetID
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ func (app *Application) buffered_render_tweet_page(w http.ResponseWriter, tpl_fi
|
|||||||
"tweet": data.Tweet,
|
"tweet": data.Tweet,
|
||||||
"user": data.User,
|
"user": data.User,
|
||||||
"retweet": data.Retweet,
|
"retweet": data.Retweet,
|
||||||
|
"space": data.Space,
|
||||||
"active_user": app.get_active_user,
|
"active_user": app.get_active_user,
|
||||||
"focused_tweet_id": data.FocusedTweetID,
|
"focused_tweet_id": data.FocusedTweetID,
|
||||||
}),
|
}),
|
||||||
@ -99,6 +101,7 @@ func (app *Application) buffered_render_tweet_htmx(w http.ResponseWriter, tpl_na
|
|||||||
"tweet": data.Tweet,
|
"tweet": data.Tweet,
|
||||||
"user": data.User,
|
"user": data.User,
|
||||||
"retweet": data.Retweet,
|
"retweet": data.Retweet,
|
||||||
|
"space": data.Space,
|
||||||
"active_user": app.get_active_user,
|
"active_user": app.get_active_user,
|
||||||
"focused_tweet_id": data.FocusedTweetID,
|
"focused_tweet_id": data.FocusedTweetID,
|
||||||
}),
|
}),
|
||||||
|
@ -172,6 +172,9 @@ func (t TweetDetailData) User(id scraper.UserID) scraper.User {
|
|||||||
func (t TweetDetailData) Retweet(id scraper.TweetID) scraper.Retweet {
|
func (t TweetDetailData) Retweet(id scraper.TweetID) scraper.Retweet {
|
||||||
return t.Retweets[id]
|
return t.Retweets[id]
|
||||||
}
|
}
|
||||||
|
func (t TweetDetailData) Space(id scraper.SpaceID) scraper.Space {
|
||||||
|
return t.Spaces[id]
|
||||||
|
}
|
||||||
func (t TweetDetailData) FocusedTweetID() scraper.TweetID {
|
func (t TweetDetailData) FocusedTweetID() scraper.TweetID {
|
||||||
return t.MainTweetID
|
return t.MainTweetID
|
||||||
}
|
}
|
||||||
@ -254,7 +257,9 @@ func (t UserProfileData) User(id scraper.UserID) scraper.User {
|
|||||||
func (t UserProfileData) Retweet(id scraper.TweetID) scraper.Retweet {
|
func (t UserProfileData) Retweet(id scraper.TweetID) scraper.Retweet {
|
||||||
return t.Retweets[id]
|
return t.Retweets[id]
|
||||||
}
|
}
|
||||||
|
func (t UserProfileData) Space(id scraper.SpaceID) scraper.Space {
|
||||||
|
return t.Spaces[id]
|
||||||
|
}
|
||||||
func (t UserProfileData) FocusedTweetID() scraper.TweetID {
|
func (t UserProfileData) FocusedTweetID() scraper.TweetID {
|
||||||
return scraper.TweetID(0)
|
return scraper.TweetID(0)
|
||||||
}
|
}
|
||||||
|
@ -217,6 +217,14 @@ func TestTweetsWithContent(t *testing.T) {
|
|||||||
root, err = html.Parse(resp.Body)
|
root, err = html.Parse(resp.Body)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Len(cascadia.QueryAll(root, selector(".embedded-link")), 3)
|
assert.Len(cascadia.QueryAll(root, selector(".embedded-link")), 3)
|
||||||
|
|
||||||
|
// Space
|
||||||
|
resp = do_request(httptest.NewRequest("GET", "/tweet/1624833173514293249", nil))
|
||||||
|
require.Equal(resp.StatusCode, 200)
|
||||||
|
root, err = html.Parse(resp.Body)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Len(cascadia.QueryAll(root, selector(".space")), 1)
|
||||||
|
assert.Len(cascadia.QueryAll(root, selector("ul.space-participants-list li")), 9)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Follow and unfollow
|
// Follow and unfollow
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
--color-twitter-off-white-dark: #dae5e5; /* hsv(180, 4.8, 89.8) */
|
--color-twitter-off-white-dark: #dae5e5; /* hsv(180, 4.8, 89.8) */
|
||||||
--color-outline-gray: #dcdcdc;
|
--color-outline-gray: #dcdcdc;
|
||||||
|
|
||||||
|
--color-space-purple: #a49bfd;
|
||||||
|
--color-space-purple-outline: #6452fc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const QColor COLOR_OUTLINE_GRAY = QColor(220, 220, 220);
|
const QColor COLOR_OUTLINE_GRAY = QColor(220, 220, 220);
|
||||||
const QColor COLOR_TWITTER_BLUE = QColor(27, 149, 224);
|
const QColor COLOR_TWITTER_BLUE = QColor(27, 149, 224);
|
||||||
@ -48,6 +51,20 @@ input, select {
|
|||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.inline-dotted-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
ul.inline-dotted-list li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
ul.inline-dotted-list li:after {
|
||||||
|
content: " ⋅";
|
||||||
|
}
|
||||||
|
ul.inline-dotted-list li:last-child:after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
.tweet {
|
.tweet {
|
||||||
padding: 0 1.5em;
|
padding: 0 1.5em;
|
||||||
}
|
}
|
||||||
@ -118,20 +135,10 @@ input, select {
|
|||||||
margin: 0 5em;
|
margin: 0 5em;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
ul.reply-mentions {
|
.reply-mentions {
|
||||||
list-style: none;
|
|
||||||
padding: 0 0.5em;
|
padding: 0 0.5em;
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
ul.reply-mentions li {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
ul.reply-mentions li:after {
|
|
||||||
content: " ⋅";
|
|
||||||
}
|
|
||||||
ul.reply-mentions li:last-child:after {
|
|
||||||
content: "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.replying-to-label {
|
.replying-to-label {
|
||||||
color: var(--color-twitter-text-gray);
|
color: var(--color-twitter-text-gray);
|
||||||
}
|
}
|
||||||
@ -170,7 +177,7 @@ img.embedded-link-preview {
|
|||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.embedded-link-title {
|
h3 {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
.embedded-link-domain-container {
|
.embedded-link-domain-container {
|
||||||
@ -474,15 +481,52 @@ input[type="submit"] {
|
|||||||
margin: 0 0.5em;
|
margin: 0 0.5em;
|
||||||
}
|
}
|
||||||
.poll-choice-label {
|
.poll-choice-label {
|
||||||
/* flex-grow: 1;*/
|
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
.poll-choice-votes {
|
.poll-choice-votes {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
/* flex-grow: 1;*/
|
|
||||||
}
|
}
|
||||||
.poll-metadata {
|
.poll-metadata {
|
||||||
color: var(--color-twitter-text-gray);
|
color: var(--color-twitter-text-gray);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space {
|
||||||
|
outline: 1px solid var(--color-space-purple-outline);
|
||||||
|
background-color: var(--color-space-purple);
|
||||||
|
border-radius: 1.5em;
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
||||||
|
.space-title {
|
||||||
|
padding-top: 0.5em;
|
||||||
|
}
|
||||||
|
.space .host-label {
|
||||||
|
color: var(--color-space-purple-outline);
|
||||||
|
}
|
||||||
|
.space-date {
|
||||||
|
color: var(--color-space-purple-outline);
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
.space-info-list {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.space .layout-spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
ul.space-participants-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
ul.space-participants-list li {
|
||||||
|
padding: 0.5em 0;
|
||||||
|
display: inline-block;
|
||||||
|
width: 24%;
|
||||||
|
}
|
||||||
|
.space-participants-list .author-info {
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
}
|
||||||
|
.space-participants-list .author-info .profile-image {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
{{if $main_tweet.ReplyMentions}}
|
{{if $main_tweet.ReplyMentions}}
|
||||||
<div class="reply-mentions-container" hx-trigger="click consume">
|
<div class="reply-mentions-container" hx-trigger="click consume">
|
||||||
<span class="replying-to-label">Replying to</span>
|
<span class="replying-to-label">Replying to</span>
|
||||||
<ul class="reply-mentions">
|
<ul class="reply-mentions inline-dotted-list">
|
||||||
{{range $main_tweet.ReplyMentions}}
|
{{range $main_tweet.ReplyMentions}}
|
||||||
<li><a class="entity" href="/{{.}}">@{{.}}</a></li>
|
<li><a class="entity" href="/{{.}}">@{{.}}</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
@ -88,6 +88,9 @@
|
|||||||
{{template "tweet" (dict "TweetID" $main_tweet.QuotedTweetID "RetweetID" 0 "QuoteNestingLevel" (add .QuoteNestingLevel 1))}}
|
{{template "tweet" (dict "TweetID" $main_tweet.QuotedTweetID "RetweetID" 0 "QuoteNestingLevel" (add .QuoteNestingLevel 1))}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if $main_tweet.SpaceID}}
|
||||||
|
{{template "space" (space $main_tweet.SpaceID)}}
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="interactions-bar">
|
<div class="interactions-bar">
|
||||||
|
34
internal/webserver/tpl/tweet_page_includes/space.tpl
Normal file
34
internal/webserver/tpl/tweet_page_includes/space.tpl
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{{define "space"}}
|
||||||
|
<div class="space">
|
||||||
|
<div class="space-host row">
|
||||||
|
{{template "author-info" (user .CreatedById)}}
|
||||||
|
<span class="host-label">(Host)</span>
|
||||||
|
<div class="layout-spacer"></div>
|
||||||
|
<div class="space-date">
|
||||||
|
{{.StartedAt.Format "Jan 2, 2006"}}<br>{{.StartedAt.Format "3:04pm"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 class="space-title">{{.Title}}</h3>
|
||||||
|
<div class="space-info row">
|
||||||
|
<span class="space-state">
|
||||||
|
{{if (eq .State "Ended")}}
|
||||||
|
<ul class="space-info-list inline-dotted-list">
|
||||||
|
<li>{{.State}}</li>
|
||||||
|
<li>{{(len .ParticipantIds)}} participants</li>
|
||||||
|
<li>{{.LiveListenersCount}} tuned in</li>
|
||||||
|
<li>Lasted {{.FormatDuration}}</li>
|
||||||
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
{{.State}}
|
||||||
|
{{end}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ul class="space-participants-list">
|
||||||
|
{{range .ParticipantIds}}
|
||||||
|
{{if (ne . $.CreatedById)}}
|
||||||
|
<li>{{template "author-info" (user .)}}</li>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
@ -45,6 +45,33 @@ func (p Profile) fill_content(trove *TweetTrove) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
space_ids := []interface{}{}
|
||||||
|
for _, t := range trove.Tweets {
|
||||||
|
if t.SpaceID != "" {
|
||||||
|
space_ids = append(space_ids, t.SpaceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(space_ids) > 0 {
|
||||||
|
var spaces []Space
|
||||||
|
err := p.DB.Select(&spaces, `
|
||||||
|
select id, created_by_id, short_url, state, title, created_at, started_at, ended_at, updated_at, is_available_for_replay,
|
||||||
|
replay_watch_count, live_listeners_count, is_details_fetched
|
||||||
|
from spaces
|
||||||
|
where id in (`+strings.Repeat("?,", len(space_ids)-1)+`?)`,
|
||||||
|
space_ids...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, s := range spaces {
|
||||||
|
err := p.DB.Select(&s.ParticipantIds, "select user_id from space_participants where space_id = ?", s.ID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
trove.Spaces[s.ID] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
in_clause := ""
|
in_clause := ""
|
||||||
user_ids := []interface{}{}
|
user_ids := []interface{}{}
|
||||||
tweet_ids := []interface{}{}
|
tweet_ids := []interface{}{}
|
||||||
@ -58,6 +85,12 @@ func (p Profile) fill_content(trove *TweetTrove) {
|
|||||||
for _, r := range trove.Retweets {
|
for _, r := range trove.Retweets {
|
||||||
user_ids = append(user_ids, int(r.RetweetedByID))
|
user_ids = append(user_ids, int(r.RetweetedByID))
|
||||||
}
|
}
|
||||||
|
for _, s := range trove.Spaces {
|
||||||
|
user_ids = append(user_ids, s.CreatedById)
|
||||||
|
for _, p := range s.ParticipantIds {
|
||||||
|
user_ids = append(user_ids, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get all the users
|
// Get all the users
|
||||||
if len(user_ids) > 0 { // It could be a search with no results, end of feed, etc-- strings.Repeat will fail!
|
if len(user_ids) > 0 { // It could be a search with no results, end of feed, etc-- strings.Repeat will fail!
|
||||||
|
Loading…
x
Reference in New Issue
Block a user