diff --git a/assets/css/app.css b/assets/css/app.css deleted file mode 100644 index fec0b3f..0000000 --- a/assets/css/app.css +++ /dev/null @@ -1,3 +0,0 @@ -/* This file is for your main application css. */ - -@import "./phoenix.css"; diff --git a/assets/css/app.scss b/assets/css/app.scss new file mode 100644 index 0000000..804b3ba --- /dev/null +++ b/assets/css/app.scss @@ -0,0 +1,5 @@ +/* This file is for your main application css. */ + +/* @import "./phoenix.css"; */ +@import "./normalize.css"; +@import "./clacks.scss"; diff --git a/assets/css/clacks.scss b/assets/css/clacks.scss new file mode 100644 index 0000000..b05774a --- /dev/null +++ b/assets/css/clacks.scss @@ -0,0 +1,139 @@ +$sans-serif: -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; +$serif: "Times New Roman", serif; +$tint-color: #4b43e0; +// $link-color: blue; +$link-color: $tint-color; +$hr-color: darkgray; + +body { + font-family: $sans-serif; + // always show scrollbar so effective page width doesn't change + overflow-y: scroll; +} + +.container { + max-width: 720px; + margin: 0 auto; +} + +a, +button.btn-link { + background: none; + border: none; + color: $link-color; + text-decoration: none; + + &:hover { + cursor: pointer; + text-decoration: underline; + } +} + +button[type=submit]:not(.btn-link) { + font-family: $sans-serif; + padding: 0.25rem 2rem; + background-color: $tint-color; + border: 1px solid darken($tint-color, 20%); + color: white; + + &:hover { + cursor: pointer; + background-color: darken($tint-color, 10%); + } +} + +hr { + border: 1px solid $hr-color; + margin: 0.75rem 0; +} + +input:focus, textarea:focus { + outline: none; + border: 2px solid $tint-color; +} + +h1, h2, h3 { + margin: 0.5rem 0; +} + +header { + nav { + display: flex; + flex-direction: row; + justify-content: space-between; + + ul { + display: flex; + padding: 0; + + li { + display: inline; + list-style: none; + } + } + } +} + +ul.status-list { + padding: 0; + margin: 0; + + li { + list-style: none; + margin-bottom: 1rem; + } +} + +.status { + padding: 0.5rem; + border: 1px solid #ddd; + background-color: #f2f2f2; + + .status-meta { + display: flex; + flex-direction: row; + align-items: flex-start; + + .status-author-nickname, + .status-author-username, + .status-meta-right { + display: inline; + margin: 0; + margin-right: 1rem; + font-size: 1rem; + } + + .status-author-username { + font-weight: normal; + } + + .status-meta-right { + flex-grow: 1; + text-align: right; + } + } + + .status-content { + font-family: $serif; + font-size: 1.2rem; + margin: 1rem 0; + + a { + text-decoration: underline; + } + } +} + +.compose-status { + textarea { + display: block; + min-width: 100%; + max-width: 100%; + min-height: 4rem; + margin-bottom: 0.75rem; + padding: 0.5rem; + + // we want 100% width to include the border + box-sizing: border-box; + } +} diff --git a/assets/css/normalize.css b/assets/css/normalize.css new file mode 100644 index 0000000..9d9f37a --- /dev/null +++ b/assets/css/normalize.css @@ -0,0 +1,350 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} + diff --git a/assets/js/app.js b/assets/js/app.js index 8a5d386..1c025d6 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,7 +1,7 @@ // We need to import the CSS so that webpack will load it. // The MiniCssExtractPlugin is used to separate it out into // its own CSS file. -import css from "../css/app.css" +import css from "../css/app.scss" // webpack automatically bundles all modules in your // entry points. Those entry points can be configured diff --git a/assets/package-lock.json b/assets/package-lock.json index 0ae3121..d039f3a 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -2779,6 +2779,17 @@ "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", "dev": true }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -9332,6 +9343,36 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sass": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.3.tgz", + "integrity": "sha512-5NMHI1+YFYw4sN3yfKjpLuV9B5l7MqQ6FlkTcC4FT+oHbBRUZoSjHrrt/mE0nFXJyY2kQtU9ou9HxvFVjLFuuw==", + "dev": true, + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -9412,6 +9453,15 @@ "safe-buffer": "^5.0.1" } }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", diff --git a/assets/package.json b/assets/package.json index a4ba0e7..339f4df 100644 --- a/assets/package.json +++ b/assets/package.json @@ -19,6 +19,8 @@ "optimize-css-assets-webpack-plugin": "^4.0.0", "uglifyjs-webpack-plugin": "^1.2.4", "webpack": "4.4.0", - "webpack-cli": "^2.0.10" + "webpack-cli": "^2.0.10", + "sass": "^1.26.3", + "sass-loader": "^7.3.1" } } diff --git a/assets/webpack.config.js b/assets/webpack.config.js index 63c1d20..45edd50 100644 --- a/assets/webpack.config.js +++ b/assets/webpack.config.js @@ -29,8 +29,8 @@ module.exports = (env, options) => ({ } }, { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] + test: /\.scss$/, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'] } ] }, diff --git a/lib/clacks_web/controllers/frontend_controller.ex b/lib/clacks_web/controllers/frontend_controller.ex index 2b3c02e..2f945ed 100644 --- a/lib/clacks_web/controllers/frontend_controller.ex +++ b/lib/clacks_web/controllers/frontend_controller.ex @@ -55,7 +55,7 @@ defmodule ClacksWeb.FrontendController do "type" => "Create", "object" => %{"type" => "Note", "attributedTo" => author_id} = note } = data - } <- Activity.get(id), + } = activity <- Activity.get(id), %Actor{} = author <- Actor.get_by_ap_id(author_id) do case conn.assigns[:format] do "activity+json" -> @@ -64,7 +64,7 @@ defmodule ClacksWeb.FrontendController do "html" -> render(conn, "status.html", %{ current_user: current_user, - note: note, + status: activity, author: author }) end @@ -99,13 +99,13 @@ defmodule ClacksWeb.FrontendController do with %Activity{ data: %{ "type" => "Create", - "object" => %{"type" => "Note", "attributedTo" => author_id} = note + "object" => %{"type" => "Note", "attributedTo" => author_id} } - } <- Activity.get(id), + } = activity <- Activity.get(id), %Actor{} = author <- Actor.get_by_ap_id(author_id) do - render(conn, "reply.html", %{ + render(conn, "status.html", %{ current_user: current_user, - note: note, + status: activity, author: author }) else diff --git a/lib/clacks_web/router.ex b/lib/clacks_web/router.ex index c34b852..45dc602 100644 --- a/lib/clacks_web/router.ex +++ b/lib/clacks_web/router.ex @@ -58,6 +58,8 @@ defmodule ClacksWeb.Router do pipe_through :browser_authenticated post "/post", FrontendController, :post_status + + get "/status/:id/reply", FrontendController, :reply end scope "/", ClacksWeb do diff --git a/lib/clacks_web/templates/frontend/_status.html.eex b/lib/clacks_web/templates/frontend/_status.html.eex index 7bc9888..93dcc77 100644 --- a/lib/clacks_web/templates/frontend/_status.html.eex +++ b/lib/clacks_web/templates/frontend/_status.html.eex @@ -1,16 +1,24 @@
-

- - <%= @author.data["preferredUsername"] %> - -

-

- - <%= @author.data["name"] %> - -

- ">Permalink +
+

+ + <%= @author.data["preferredUsername"] %> + +

+

+ + <%= display_username(@author) %> + +

+

+ <%= display_timestamp(@note["published"]) %> + " class="status-permalink">Permalink +

+
<%= @note["content"] %>
+
+ Reply +
diff --git a/lib/clacks_web/templates/frontend/_timeline.html.eex b/lib/clacks_web/templates/frontend/_timeline.html.eex index b38efa0..2ee54a8 100644 --- a/lib/clacks_web/templates/frontend/_timeline.html.eex +++ b/lib/clacks_web/templates/frontend/_timeline.html.eex @@ -1,7 +1,7 @@ - diff --git a/lib/clacks_web/views/frontend_view.ex b/lib/clacks_web/views/frontend_view.ex index c811d5c..1d5d57c 100644 --- a/lib/clacks_web/views/frontend_view.ex +++ b/lib/clacks_web/views/frontend_view.ex @@ -1,3 +1,46 @@ defmodule ClacksWeb.FrontendView do use ClacksWeb, :view + alias Clacks.Actor + + @spec display_username(actor :: Actor.t()) :: String.t() + + def display_username(%Actor{local: true, data: %{"name" => name}}) do + "@" <> name + end + + def display_username(%Actor{local: false, ap_id: ap_id, data: %{"name" => name}}) do + %URI{host: host} = URI.parse(ap_id) + "@" <> name <> "@" <> host + end + + @absolute_timestamp_threshold 24 * 60 * 60 + + def display_timestamp(str) when is_binary(str) do + display_timestamp(Timex.parse!(str, "{ISO:Extended}")) + end + + def display_timestamp(datetime) do + diff = Timex.diff(Timex.now(), datetime, :seconds) + + cond do + diff < 60 -> + # less than a minute, seconds + "#{diff}sec" + + diff < 60 * 60 -> + # less than an hour, minutes + "#{Integer.floor_div(diff, 60)}min" + + diff < 60 * 60 * 24 -> + # less than a day, hours + "#{Integer.floor_div(diff, 60 * 60)}hr" + + diff < 60 * 60 * 24 * 7 -> + # less than a week, days + "#{Integer.floor_div(diff, 60 * 60 * 24)}d" + + true -> + Timex.format!(datetime, "%FT%T%:z", :strftime) + end + end end