diff --git a/src/lib/constants.js b/src/lib/constants.js index 21a3578..e680e4e 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -104,6 +104,16 @@ let constants = { default: "", boolean: true, replaceEmptyWithDefault: true + },{ + name: "timeline_columns", + default: "dynamic", + boolean: false, + replaceEmptyWithDefault: true + },{ + name: "display_top_nav", + default: "", + boolean: true, + replaceEmptyWithDefault: true } ], @@ -200,7 +210,7 @@ let constants = { additional_routes: [], - database_version: 4 + database_version: 5 } // Override values from config and export the result diff --git a/src/lib/utils/upgradedb.js b/src/lib/utils/upgradedb.js index 1416f07..1f861fe 100644 --- a/src/lib/utils/upgradedb.js +++ b/src/lib/utils/upgradedb.js @@ -60,7 +60,21 @@ const deltas = new Map([ ) .run() })() - }] + }], + // version 4 to version 5 + [5, function() { + db.transaction(() => { + // the previous version wasn't around for long enough for me to care about the contents + db.prepare("DROP TABLE IF EXISTS UserSettings") + .run() + db.prepare( + "CREATE TABLE UserSettings (token TEXT NOT NULL, created INTEGER NOT NULL, language TEXT NOT NULL, show_comments INTEGER NOT NULL, link_hashtags INTEGER NOT NULL" + +", spa INTEGER NOT NULL, theme TEXT NOT NULL, caption_side TEXT NOT NULL, display_alt INTEGER NOT NULL, timeline_columns TEXT NOT NULL, display_top_nav INTEGER NOT NULL" + +", PRIMARY KEY (token))" + ) + .run() + })() + }], ]) module.exports = async function() { diff --git a/src/site/api/settings.js b/src/site/api/settings.js index 83d2f53..e84c444 100644 --- a/src/site/api/settings.js +++ b/src/site/api/settings.js @@ -40,10 +40,8 @@ module.exports = [ prepared.token = crypto.randomBytes(16).toString("hex") } while (checkPrepared.get(prepared.token)) prepared.created = Date.now() - db.prepare( - "INSERT INTO UserSettings (token, created, language, show_comments, link_hashtags, spa, theme, caption_side, display_alt)" - +" VALUES (@token, @created, @language, @show_comments, @link_hashtags, @spa, @theme, @caption_side, @display_alt)" - ).run(prepared) + const fields = constants.user_settings.map(s => s.name) + db.prepare(`INSERT INTO UserSettings (token, created, ${fields.join(", ")}) VALUES (@token, @created, ${fields.map(f => "@"+f).join(", ")})`).run(prepared) const expires = new Date(Date.now() + 4000*24*60*60*1000).toUTCString() return { statusCode: 303, diff --git a/src/site/api/utils/getsettings.js b/src/site/api/utils/getsettings.js index d2b2ee1..c61f55b 100644 --- a/src/site/api/utils/getsettings.js +++ b/src/site/api/utils/getsettings.js @@ -9,7 +9,11 @@ function addDefaults(input = {}) { if (input[setting.name] !== undefined) { result[setting.name] = input[setting.name] } else { - result[setting.name] = setting.default + if (setting.boolean) { + result[setting.name] = +(setting.default !== "") + } else { + result[setting.name] = setting.default + } } } return result diff --git a/src/site/html/static/img/logo-circle-min.svg b/src/site/html/static/img/logo-circle-min.svg new file mode 100644 index 0000000..35b6564 --- /dev/null +++ b/src/site/html/static/img/logo-circle-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/site/html/static/img/settings.svg b/src/site/html/static/img/settings.svg new file mode 100644 index 0000000..1f8e640 --- /dev/null +++ b/src/site/html/static/img/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/site/pug/settings.pug b/src/site/pug/settings.pug index e8162ec..f8d0f73 100644 --- a/src/site/pug/settings.pug +++ b/src/site/pug/settings.pug @@ -56,6 +56,15 @@ html {value: "classic", text: "Classic"} ]) + +checkbox("display_top_nav", "Display top bar", "Always", false) + + +select("timeline_columns", "Timeline columns", true, [ + {value: "dynamic", text: "Dynamic"}, + {value: "3", text: "3 columns"}, + {value: "4", text: "4 columns"}, + {value: "6", text: "6 columns"} + ]) + +select("caption_side", "Caption side", true, [ {value: "left", text: "Left (Bibliogram)"}, {value: "right", text: "Right (Instagram)"} diff --git a/src/site/pug/user.pug b/src/site/pug/user.pug index 13353f5..ec2fea9 100644 --- a/src/site/pug/user.pug +++ b/src/site/pug/user.pug @@ -31,39 +31,50 @@ html meta(property="og:site_name" content="Bibliogram") body + nav(class=(settings.display_top_nav ? "always-displayed" : "")).top-nav + //- Alt text guidelines from https://axesslab.com/alt-texts/ + a(href="/").nav-icon-link + img(src="/static/img/logo-circle-min.svg" alt="Bibliogram").logo + a(href="/settings").nav-icon-link + img(src="/static/img/settings.svg" alt="Settings").settings .main-divider header.profile-overview .profile-sticky - img(src=user.proxyProfilePicture width=150 height=150 alt=`${user.data.full_name || user.data.username}'s profile picture.`).pfp - //- - Instagram only uses the above URL, but an HD version is also available. - The alt text is pathetic, I know. I don't have much to work with. - if user.data.full_name - h1.full-name= user.data.full_name - h2.username= `@${user.data.username}` - else - h1.full-name= `@${user.data.username}` - p.structured-text.bio - - const bio = user.getStructuredBio() - if bio - +display_structured(bio) - if user.data.external_url - p.website - a(href=user.data.external_url)= user.data.external_url - if user.posts != undefined - div.profile-counter #[span(data-numberformat=user.posts).count #{numberFormat(user.posts)}] posts - if followerCountsAvailable - if user.following != undefined - div.profile-counter #[span(data-numberformat=user.following).count #{numberFormat(user.following)}] following - if user.followedBy != undefined - div.profile-counter #[span(data-numberformat=user.followedBy).count #{numberFormat(user.followedBy)}] followed by - else - div.profile-counter.not-available Followers not available. - div.links - if constants.feeds.enabled && constants.feeds.display_links - +feed_link("RSS", "rss", user.data.username, "application/rss+xml", constants.feeds.display_validation_links) - +feed_link("Atom", "atom", user.data.username, "application/atom+xml", constants.feeds.display_validation_links) - a(rel="noreferrer noopener" href=`https://www.instagram.com/${user.data.username}` target="_blank") instagram.com + section + img(src=user.proxyProfilePicture width=150 height=150 alt=`${user.data.full_name || user.data.username}'s profile picture.`).pfp + //- + Instagram only uses the above URL, but an HD version is also available. + The alt text is pathetic, I know. I don't have much to work with. + if user.data.full_name + h1.full-name= user.data.full_name + h2.username= `@${user.data.username}` + else + h1.full-name= `@${user.data.username}` + p.structured-text.bio + - const bio = user.getStructuredBio() + if bio + +display_structured(bio) + if user.data.external_url + p.website + a(href=user.data.external_url)= user.data.external_url + if user.posts != undefined + div.profile-counter #[span(data-numberformat=user.posts).count #{numberFormat(user.posts)}] posts + if followerCountsAvailable + if user.following != undefined + div.profile-counter #[span(data-numberformat=user.following).count #{numberFormat(user.following)}] following + if user.followedBy != undefined + div.profile-counter #[span(data-numberformat=user.followedBy).count #{numberFormat(user.followedBy)}] followed by + else + div.profile-counter.not-available Followers not available. + .links + if constants.feeds.enabled && constants.feeds.display_links + +feed_link("RSS", "rss", user.data.username, "application/rss+xml", constants.feeds.display_validation_links) + +feed_link("Atom", "atom", user.data.username, "application/atom+xml", constants.feeds.display_validation_links) + a(rel="noreferrer noopener" href=`https://www.instagram.com/${user.data.username}` target="_blank") instagram.com + section.bibliogram-meta + .links + a(href="/") Home + a(href="/settings") Settings - const hasPosts = !user.data.is_private && user.timeline.pages.length && user.timeline.pages[0].length main(class=hasPosts ? "" : "no-posts")#timeline.timeline diff --git a/src/site/sass/main.sass b/src/site/sass/main.sass index de831c0..a73f3d4 100644 --- a/src/site/sass/main.sass +++ b/src/site/sass/main.sass @@ -7,6 +7,7 @@ $layout-home-a-max: 520px $layout-home-b-min: 521px $main-theme-link-color: #085cae $medium-red-bg: #6a2222 +$light-red-bg: #bd4444 $solar-background: #fff4e8 @font-face @@ -51,6 +52,35 @@ body background: hsl(102.1, 77.2%, 67.3%) border-color: hsl(104, 51.4%, 43.5%) +.top-nav + background-color: $light-red-bg + position: relative + z-index: 1 + box-shadow: 0px -2px 4px 4px rgba(0, 0, 0, 0.4) + border-bottom: 1px solid #333 + padding: 6px 12px + justify-content: space-between + align-items: center + + display: none + + @media screen and (max-width: $layout-a-max) + display: flex + + &.always-displayed + display: flex + + .logo + width: 48px + height: 48px + + .settings + width: 36px + height: 36px + + .nav-icon-link + display: flex + .profile-overview text-align: center position: relative @@ -78,6 +108,9 @@ body @media screen and (max-width: $layout-a-max) height: unset + a, a:visited + color: $main-theme-link-color + .pfp margin: 25px 0 @@ -106,9 +139,6 @@ body .website margin: 20px 0px - a, a:visited - color: $main-theme-link-color - .links margin: 15px 0px display: flex @@ -118,11 +148,17 @@ body .validate-feed margin-left: 2px - a, a:visited - color: $main-theme-link-color - > * - margin: 5px + margin: 5px 8px + + .bibliogram-meta + margin: 20px 10px + border-top: 1px solid #333 + + @media screen and (max-width: $layout-a-max) + display: none + + .timeline --image-size: 260px @@ -260,6 +296,10 @@ body padding: 10px position: sticky top: 0 + border-bottom: 1px solid #333 + + @media screen and (max-width: $layout-a-max) + box-shadow: 0px -2px 4px 4px rgba(0, 0, 0, 0.4) .navigate-posts -webkit-appearance: none @@ -306,6 +346,7 @@ body @media screen and (min-width: $layout-b-min) .relative-box position: relative + box-shadow: 0px 6px 4px -4px rgba(0, 0, 0, .4) inset .scrolling-box position: absolute @@ -401,7 +442,7 @@ body display: flex flex-direction: column min-height: 100vh - background-color: #bd4444 + background-color: $light-red-bg color: #fff h1