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