Do jam showcase stuff

This commit is contained in:
Ben Visness 2021-09-24 22:53:00 -05:00
parent 776c78913a
commit 251446d6e4
9 changed files with 253 additions and 10 deletions

View File

@ -72,10 +72,11 @@ type DiscordConfig struct {
OAuthClientID string
OAuthClientSecret string
GuildID string
MemberRoleID string
ShowcaseChannelID string
LibraryChannelID string
GuildID string
MemberRoleID string
ShowcaseChannelID string
LibraryChannelID string
JamShowcaseChannelID string
}
type EpisodeGuide struct {

View File

@ -582,7 +582,7 @@ func (bot *botInstance) messageCreateOrUpdate(ctx context.Context, msg *Message)
}
if msg.ChannelID == config.Config.Discord.ShowcaseChannelID {
err := bot.processShowcaseMsg(ctx, msg)
err := bot.processShowcaseMsg(ctx, msg, false)
if err != nil {
logging.ExtractLogger(ctx).Error().Err(err).Msg("failed to process showcase message")
return nil
@ -590,6 +590,15 @@ func (bot *botInstance) messageCreateOrUpdate(ctx context.Context, msg *Message)
return nil
}
if msg.ChannelID == config.Config.Discord.JamShowcaseChannelID {
err := bot.processShowcaseMsg(ctx, msg, true)
if err != nil {
logging.ExtractLogger(ctx).Error().Err(err).Msg("failed to process jam showcase message")
return nil
}
return nil
}
if msg.ChannelID == config.Config.Discord.LibraryChannelID {
err := bot.processLibraryMsg(ctx, msg)
if err != nil {

View File

@ -25,7 +25,8 @@ var reDiscordMessageLink = regexp.MustCompile(`https?://.+?(\s|$)`)
var errNotEnoughInfo = errors.New("Discord didn't send enough info in this event for us to do this")
func (bot *botInstance) processShowcaseMsg(ctx context.Context, msg *Message) error {
// TODO: Turn this ad-hoc isJam parameter into a tag or something
func (bot *botInstance) processShowcaseMsg(ctx context.Context, msg *Message, isJam bool) error {
switch msg.Type {
case MessageTypeDefault, MessageTypeReply, MessageTypeApplicationCommand:
default:
@ -57,10 +58,17 @@ func (bot *botInstance) processShowcaseMsg(ctx context.Context, msg *Message) er
return err
}
if doSnippet, err := AllowedToCreateMessageSnippet(ctx, tx, newMsg.UserID); doSnippet && err == nil {
_, err := CreateMessageSnippet(ctx, tx, msg.ID)
snippet, err := CreateMessageSnippet(ctx, tx, msg.ID)
if err != nil {
return oops.New(err, "failed to create snippet in gateway")
}
if isJam {
_, err := tx.Exec(ctx, `UPDATE handmade_snippet SET is_jam = TRUE WHERE id = $1`, snippet.ID)
if err != nil {
return oops.New(err, "failed to mark snippet as a jam snippet")
}
}
} else if err != nil {
return oops.New(err, "failed to check snippet permissions in gateway")
}

View File

@ -0,0 +1,52 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"git.handmade.network/hmn/hmn/src/oops"
"github.com/jackc/pgx/v4"
)
func init() {
registerMigration(AddJamSnippetField{})
}
type AddJamSnippetField struct{}
func (m AddJamSnippetField) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2021, 9, 25, 2, 38, 10, 0, time.UTC))
}
func (m AddJamSnippetField) Name() string {
return "AddJamSnippetField"
}
func (m AddJamSnippetField) Description() string {
return "Add a special field for jam snippets"
}
func (m AddJamSnippetField) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx, `
ALTER TABLE handmade_snippet
ADD is_jam BOOLEAN NOT NULL DEFAULT FALSE;
`)
if err != nil {
return oops.New(err, "failed to add jam column")
}
return nil
}
func (m AddJamSnippetField) Down(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx, `
ALTER TABLE handmade_snippet
DROP is_jam;
`)
if err != nil {
return oops.New(err, "failed to drop jam column")
}
return nil
}

View File

@ -22,12 +22,19 @@
<title>Handmade Network</title>
{{ end }}
<script src="{{ static "js/templates.js" }}"></script>
<script src="{{ static "js/showcase.js" }}"></script>
<link rel="stylesheet" href="{{ static "fonts/mohave/stylesheet.css" }}">
<link href='https://fonts.googleapis.com/css?family=Fira+Sans:300,400,500,600' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Fira+Mono:300,400,500,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="{{ static "style.css" }}">
<style>
:root {
--content-background: #f8f8f8;
}
body {
background: linear-gradient(#ab4c47, #a5467d);
}
@ -171,6 +178,11 @@
text-transform: uppercase;
}
.showcase-item {
background-color: rgba(0, 0, 0, 0.2);
border-color: rgba(255, 255, 255, 0.5);
}
@media screen and (min-width: 30em) {
/* not small styles */
@ -256,6 +268,16 @@
</p>
</div>
<div id="showcase-outer-container" class="bg-black-20 pt4 pb3 pb4-ns">
<div class="section mw8 margin-center ph3 ph4-l">
<h2>Happening right now.</h2>
<p>
The jam is underway! These screenshots and videos were shared in #jam-showcase on our <a href="https://discord.gg/hxWxDee" target="_blank">Discord</a>. Join us!
</p>
<div id="showcase-container" class="mw8 center-layout mh2 mh0-ns"></div>
</div>
</div>
<div class="section flex-fair mw8 margin-center ph3 ph4-l mv4">
<h2>Why reinvent the wheel?</h2>
<p>
@ -346,6 +368,106 @@
{{ template "footer.html" . }}
</div>
</div>
{{ template "showcase_templates.html" }}
<!-- Copy-pasted and mangled from showcase.html -->
<script>
const ROW_HEIGHT = 300;
const ITEM_SPACING = 4;
const showcaseItems = JSON.parse("{{ .ShowcaseItemsJSON }}");
const addThumbnailFuncs = new Array(showcaseItems.length);
const showcaseOuterContainer = document.querySelector('#showcase-outer-container');
let showcaseContainer = document.querySelector('#showcase-container');
showcaseOuterContainer.classList.toggle('dn', showcaseItems.length === 0);
const itemElements = []; // array of arrays
for (let i = 0; i < showcaseItems.length; i++) {
const item = showcaseItems[i];
const [itemEl, addThumbnail] = makeShowcaseItem(item);
itemEl.container.setAttribute('data-index', i);
itemEl.container.setAttribute('data-date', item.date);
addThumbnailFuncs[i] = addThumbnail;
itemElements.push(itemEl.container);
}
function layout() {
const width = showcaseContainer.getBoundingClientRect().width;
showcaseContainer = emptyElement(showcaseContainer);
function addRow(itemEls, rowWidth, container) {
const totalSpacing = ITEM_SPACING * (itemEls.length - 1);
const scaleFactor = (width / Math.max(rowWidth, width));
const row = document.createElement('div');
row.classList.add('flex');
row.classList.toggle('justify-between', rowWidth >= width);
row.style.marginBottom = `${ITEM_SPACING}px`;
for (const itemEl of itemEls) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
const baseWidth = (aspect * ROW_HEIGHT) * scaleFactor;
const actualWidth = baseWidth - (totalSpacing / itemEls.length);
itemEl.style.width = `${actualWidth}px`;
itemEl.style.height = `${scaleFactor * ROW_HEIGHT}px`;
itemEl.style.marginRight = `${ITEM_SPACING}px`;
row.appendChild(itemEl);
}
container.appendChild(row);
}
let rowItemEls = [];
let rowWidth = 0;
for (const itemEl of itemElements) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
rowWidth += aspect * ROW_HEIGHT;
rowItemEls.push(itemEl);
if (rowWidth > width) {
addRow(rowItemEls, rowWidth, showcaseContainer);
rowItemEls = [];
rowWidth = 0;
}
}
addRow(rowItemEls, rowWidth, showcaseContainer);
}
function loadImages() {
const items = showcaseContainer.querySelectorAll('.showcase-item');
for (const item of items) {
const i = parseInt(item.getAttribute('data-index'), 10);
addThumbnailFuncs[i]();
}
}
layout();
layout(); // scrollbars are fun!!
loadImages();
window.addEventListener('resize', () => {
layout();
});
</script>
</body>
</html>

View File

@ -238,6 +238,8 @@ func AtomFeed(c *RequestContext) ResponseData {
INNER JOIN auth_user AS owner ON owner.id = snippet.owner_id
LEFT JOIN handmade_asset AS asset ON asset.id = snippet.asset_id
LEFT JOIN handmade_discordmessage AS discord_message ON discord_message.id = snippet.discord_message_id
WHERE
NOT snippet.is_jam
ORDER BY snippet.when DESC
LIMIT $1
`,

View File

@ -1,9 +1,13 @@
package website
import (
"net/http"
"time"
"git.handmade.network/hmn/hmn/src/db"
"git.handmade.network/hmn/hmn/src/hmnurl"
"git.handmade.network/hmn/hmn/src/models"
"git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/templates"
)
@ -16,6 +20,45 @@ func JamIndex(c *RequestContext) ResponseData {
daysUntil = 0
}
c.Perf.StartBlock("SQL", "Fetch showcase snippets")
type snippetQuery struct {
Owner models.User `db:"owner"`
Snippet models.Snippet `db:"snippet"`
Asset *models.Asset `db:"asset"`
DiscordMessage *models.DiscordMessage `db:"discord_message"`
}
snippetQueryResult, err := db.Query(c.Context(), c.Conn, snippetQuery{},
`
SELECT $columns
FROM
handmade_snippet AS snippet
INNER JOIN auth_user AS owner ON owner.id = snippet.owner_id
LEFT JOIN handmade_asset AS asset ON asset.id = snippet.asset_id
LEFT JOIN handmade_discordmessage AS discord_message ON discord_message.id = snippet.discord_message_id
WHERE
snippet.is_jam
ORDER BY snippet.when DESC
LIMIT 20
`,
)
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam snippets"))
}
snippetQuerySlice := snippetQueryResult.ToSlice()
showcaseItems := make([]templates.TimelineItem, 0, len(snippetQuerySlice))
for _, s := range snippetQuerySlice {
row := s.(*snippetQuery)
timelineItem := SnippetToTimelineItem(&row.Snippet, row.Asset, row.DiscordMessage, &row.Owner, c.Theme)
if timelineItem.Type != templates.TimelineTypeSnippetYoutube {
showcaseItems = append(showcaseItems, timelineItem)
}
}
c.Perf.EndBlock()
c.Perf.StartBlock("SHOWCASE", "Convert to json")
showcaseJson := templates.TimelineItemsToJSON(showcaseItems)
c.Perf.EndBlock()
baseData := getBaseDataAutocrumb(c, "Wheel Reinvention Jam")
baseData.OpenGraphItems = []templates.OpenGraphItem{
{Property: "og:site_name", Value: "Handmade.Network"},
@ -27,12 +70,14 @@ func JamIndex(c *RequestContext) ResponseData {
type JamPageData struct {
templates.BaseData
DaysUntil int
DaysUntil int
ShowcaseItemsJSON string
}
res.MustWriteTemplate("wheeljam_index.html", JamPageData{
BaseData: baseData,
DaysUntil: daysUntil,
BaseData: baseData,
DaysUntil: daysUntil,
ShowcaseItemsJSON: showcaseJson,
}, c.Perf)
return res
}

View File

@ -232,6 +232,8 @@ func Index(c *RequestContext) ResponseData {
INNER JOIN auth_user AS owner ON owner.id = snippet.owner_id
LEFT JOIN handmade_asset AS asset ON asset.id = snippet.asset_id
LEFT JOIN handmade_discordmessage AS discord_message ON discord_message.id = snippet.discord_message_id
WHERE
NOT snippet.is_jam
ORDER BY snippet.when DESC
LIMIT 20
`,

View File

@ -32,6 +32,8 @@ func Showcase(c *RequestContext) ResponseData {
INNER JOIN auth_user AS owner ON owner.id = snippet.owner_id
LEFT JOIN handmade_asset AS asset ON asset.id = snippet.asset_id
LEFT JOIN handmade_discordmessage AS discord_message ON discord_message.id = snippet.discord_message_id
WHERE
NOT snippet.is_jam
ORDER BY snippet.when DESC
`,
)