Browse Source

feat(theme): Add 'simplest' to platform.

jackyalcine 4 months ago
parent
commit
88694b430c
Signed by: Jacky Alciné <yo@jacky.wtf> GPG Key ID: 537A4F904B15268D
37 changed files with 1105 additions and 154 deletions
  1. 4
    2
      .gitignore
  2. 1
    1
      lib/webmention.ex
  3. 1
    0
      package.json
  4. 4
    0
      priv/themes/Makefile
  5. 1
    1
      priv/themes/default/tmpl/entry/type/note.html.liquid
  6. 2
    8
      priv/themes/default/tmpl/stream/_author.html.liquid
  7. 2
    2
      priv/themes/default/tmpl/stream/_reaction.html.liquid
  8. 1
    1
      priv/themes/default/tmpl/stream/empty.html.liquid
  9. 4
    0
      priv/themes/simplest/Makefile
  10. 8
    0
      priv/themes/simplest/README.markdown
  11. 2
    0
      priv/themes/simplest/assets/theme.css
  12. 215
    0
      priv/themes/simplest/src/_layout.scss
  13. 96
    0
      priv/themes/simplest/src/_syntax-highlighting.scss
  14. 46
    0
      priv/themes/simplest/src/_variables.scss
  15. 11
    0
      priv/themes/simplest/src/base/_base.scss
  16. 37
    0
      priv/themes/simplest/src/base/_buttons.scss
  17. 90
    0
      priv/themes/simplest/src/base/_forms.scss
  18. 31
    0
      priv/themes/simplest/src/base/_lists.scss
  19. 54
    0
      priv/themes/simplest/src/base/_reset.scss
  20. 25
    0
      priv/themes/simplest/src/base/_tables.scss
  21. 105
    0
      priv/themes/simplest/src/base/_typography.scss
  22. 8
    0
      priv/themes/simplest/src/main.scss
  23. 11
    0
      priv/themes/simplest/tmpl/_footer.html.liquid
  24. 29
    0
      priv/themes/simplest/tmpl/_header.html.liquid
  25. 17
    0
      priv/themes/simplest/tmpl/entry/_comments.html.liquid
  26. 17
    0
      priv/themes/simplest/tmpl/entry/_facepile.html.liquid
  27. 23
    0
      priv/themes/simplest/tmpl/entry/_mentions.html.liquid
  28. 5
    0
      priv/themes/simplest/tmpl/entry/_reply-body.html.liquid
  29. 25
    0
      priv/themes/simplest/tmpl/entry/_reply.html.liquid
  30. 13
    0
      priv/themes/simplest/tmpl/entry/gone.html.liquid
  31. 13
    0
      priv/themes/simplest/tmpl/entry/not-found.html.liquid
  32. 60
    0
      priv/themes/simplest/tmpl/entry/view.html.liquid
  33. 1
    0
      priv/themes/simplest/tmpl/follow.html.liquid
  34. 29
    0
      priv/themes/simplest/tmpl/home.html.liquid
  35. 18
    0
      priv/themes/simplest/tmpl/stream/empty.html.liquid
  36. 28
    0
      priv/themes/simplest/tmpl/stream/page.html.liquid
  37. 68
    139
      test/integration/template_test.exs

+ 4
- 2
.gitignore View File

@@ -18,5 +18,7 @@ priv/static/doc/
18 18
 test/snapshots/*.png
19 19
 tmp/docker/
20 20
 !priv/themes/default/
21
-priv/themes/testing-*
22
-priv/static/
21
+priv/static/*
22
+/.sass-cache/
23
+.sass-cache
24
+*.map

+ 1
- 1
lib/webmention.ex View File

@@ -219,7 +219,7 @@ defmodule Koype.Webmention do
219 219
         id: id,
220 220
         source: source,
221 221
         target: target_url,
222
-        type: args[:type] || "article",
222
+        type: Keyword.get(args, :type, "note"),
223 223
         mf2: entry_mf2
224 224
       ]
225 225
 

+ 1
- 0
package.json View File

@@ -4,6 +4,7 @@
4 4
   "description": "A single-tenant IndieWeb platform.",
5 5
   "main": "index.js",
6 6
   "scripts": {
7
+    "themes:build": "make -C priv/themes all",
7 8
     "parcel:watch": "npx parcel watch web/static/*css web/static/js/*.ts web/static/images/* web/static/images/**/* --out-dir priv/static/assets --public-url '.' --cache-dir tmp/parcel --log-level 5 --global Koype --hmr-port $HMR_PORT",
8 9
     "parcel:build": "npx parcel build web/static/*css web/static/js/*.ts web/static/images/* web/static/images/**/* --out-dir priv/static/assets --public-url '.' --cache-dir tmp/parcel --global Koype --log-level 4 --detailed-report",
9 10
     "test": "jest"

+ 4
- 0
priv/themes/Makefile View File

@@ -0,0 +1,4 @@
1
+all: simplest
2
+
3
+simplest:
4
+	${MAKE} -C simplest all

+ 1
- 1
priv/themes/default/tmpl/entry/type/note.html.liquid View File

@@ -1,7 +1,7 @@
1 1
 {% assign content = entry.json.content %}{% assign html_content = content.html %}{% assign text_content = content.text %}
2 2
 {% if html_content != nil %}
3 3
 <section class="measure pa1 lh-copy f4 f3-l flex-grow flex-auto content-stretch
4
-  p-name e-content">{{ html_content }}</section>
4
+  e-name e-content">{{ html_content }}</section>
5 5
 {% elsif text_content != nil %}
6 6
 <section class="measure pa1 lh-copy f4 f3-l flex-grow flex-auto content-stretch
7 7
   p-name p-content">{{ text_content }}</section>

+ 2
- 8
priv/themes/default/tmpl/stream/_author.html.liquid View File

@@ -8,15 +8,9 @@
8 8
 <div class="w3 dib pr2 flex flex-column items-center justify-center">
9 9
   <a class="w3 dib {% if hide_author != "true" %} u-author{% endif %}" href="{{ h.card.uri }}">
10 10
   {% if author != nil and author.photo != nil %}
11
-      <img src="{{ author.photo }}" title="{{ author.name }}" class="w3 z-0" />
12
-      <img title="{{ h.card.name }}"
13
-          alt="{{ h.card.name }}"
14
-          src="{{ h.card.photo }}"
15
-          class="br-100 {% if hide_author != "true" %}u-photo o-50 glow {% endif %}mw1 z-1 relative top--2 left-1" />
11
+      <img src="{{ author.photo }}" title="{{ author.name }}" class="w3" />
16 12
   {% else %}
17
-      <img title="{{ h.card.name }}" alt="{{ h.card.name }}" src="{{ h.card.photo
18
-      }}" class="br-100 {% if hide_author != "true" %}u-photo {% endif %}w3 z-0" />
13
+      <img title="{{ h.card.name }}" alt="{{ h.card.name }}" src="{{ h.card.photo }}" class="w3" />
19 14
   {% endif %}
20
-  {% if hide_author != "true" %}<span class="p-name dn">{{ h.card.name }}</span>{% endif %}
21 15
   </a>
22 16
 </div>

+ 2
- 2
priv/themes/default/tmpl/stream/_reaction.html.liquid View File

@@ -25,7 +25,7 @@
25 25
     {{ prefix_name }}
26 26
     &nbsp;
27 27
     {% if starter != starter_content and starter_content != "" %}
28
-      <a href="{{ reaction.author.url }}" class="u-author link color-inherit h-card">{{ reaction.author.name }}</a>
28
+      <a href="{{ reaction.author.url }}" class="u-author link color-inherit">{{ reaction.author.name }}</a>
29 29
       &mdash;
30 30
       <a class="u-url link p-name link color-inherit underline" href="{{ reaction.url }}">
31 31
         {{ content_name|truncate:100 }}
@@ -38,6 +38,6 @@
38 38
   {% if reaction.summary %}
39 39
     <main class="e-summary br2 br--bottom mb2 pa2 f5 lh-copy">{{ reaction.summary }}</main>
40 40
   {% elsif content != nil and content != empty and content != blank %}
41
-    <main class="e-content {% if starter == starter_content %}e-name{% endif %} br2 br--bottom mb2 pa2 f5 lh-copy">{{ content|truncate:500 }}</main>
41
+    <main class="e-content br2 br--bottom mb2 pa2 f5 lh-copy">{{ content }}</main>
42 42
   {% endif %}
43 43
 </article>

+ 1
- 1
priv/themes/default/tmpl/stream/empty.html.liquid View File

@@ -1,4 +1,4 @@
1
-<section class="w-100 mw8 center pa3 flex flex-column flex-row-l items-start justify-center items-center content-stretch h-auto">
1
+<section class="h-feed w-100 mw8 center pa3 flex flex-column flex-row-l items-start justify-center items-center content-stretch h-auto">
2 2
   <img src="{% asset_path /images/flamenco/flamenco-list-is-empty.png %}"
3 3
         class="flex-auto flex-grow mw6 w-100 order-1"
4 4
         alt="Image of person pointing to a notepad indicating that it's empty." />

+ 4
- 0
priv/themes/simplest/Makefile View File

@@ -0,0 +1,4 @@
1
+all: build
2
+
3
+build:
4
+	scss --style compressed --trace -r bourbon src/main.scss assets/theme.css

+ 8
- 0
priv/themes/simplest/README.markdown View File

@@ -0,0 +1,8 @@
1
+# Simplest
2
+
3
+This theme is a port of "[Simplest][1]" by [Fernando Moreira][2] [[source][3]].
4
+
5
+
6
+[1]: https://github.com/nandomoreirame/simplest
7
+[2]: https://nandomoreira.dev/
8
+[3]: https://github.com/nandomoreirame/simplest

+ 2
- 0
priv/themes/simplest/assets/theme.css
File diff suppressed because it is too large
View File


+ 215
- 0
priv/themes/simplest/src/_layout.scss View File

@@ -0,0 +1,215 @@
1
+* {
2
+  &,
3
+  &:before,
4
+  &:after {
5
+    box-sizing: border-box;
6
+  }
7
+}
8
+
9
+html,
10
+body {
11
+  width: 100%;
12
+  height: 100%;
13
+}
14
+
15
+.clearfix {
16
+  &:before {
17
+    content: " ";
18
+    display: table;
19
+  }
20
+
21
+  &:after {
22
+    content: " ";
23
+    display: table;
24
+    clear: both;
25
+  }
26
+
27
+  *zoom: 1;
28
+}
29
+
30
+body {
31
+  background-color: $base-background-color;
32
+  font-weight: 400;
33
+}
34
+
35
+hr {
36
+  border-top: none;
37
+  border-bottom: 2px solid lighten($light-gray-color, 7%);
38
+  width: 100%;
39
+  margin: $small-spacing 0;
40
+}
41
+
42
+.container {
43
+  padding: $base-spacing;
44
+  margin-left: auto;
45
+  margin-right: auto;
46
+  max-width: 800px;
47
+}
48
+
49
+.pull-left {
50
+  float: left;
51
+}
52
+
53
+.pull-right {
54
+  float: right;
55
+}
56
+
57
+.link {
58
+  display: inline-block;
59
+  padding-bottom: .08em;
60
+  border-bottom: 2px solid $light-gray-color;
61
+
62
+  // @include transition(border 300ms linear);
63
+
64
+  &:hover,
65
+  &:focus,
66
+  &:active {
67
+    border-color: darken($light-gray-color, 10%);
68
+  }
69
+}
70
+
71
+.site-header {
72
+  border-top: 5px solid $light-gray-color;
73
+  border-bottom: 1px solid lighten($light-gray-color, 5%);
74
+  margin-bottom: $small-spacing;
75
+  min-height: 80px;
76
+}
77
+
78
+.intro {
79
+  .author-name {
80
+    background-color: $action-color;
81
+    color: $white-color;
82
+    display: inline-block;
83
+    padding: 0 .3em;
84
+  }
85
+
86
+  a {
87
+    @extend .link;
88
+  }
89
+}
90
+
91
+.logo {
92
+  float: left;
93
+  margin: 0 0 1em 0;
94
+  cursor: pointer;
95
+  text-transform: uppercase;
96
+  letter-spacing: 0.8px;
97
+  font-size: 20px;
98
+  line-height: 28px;
99
+  font-weight: 300;
100
+
101
+  span {
102
+    font-weight: 700;
103
+  }
104
+}
105
+
106
+.nav {
107
+  min-height: 64px;
108
+}
109
+
110
+.navbar {
111
+  float: right;
112
+  margin: 0;
113
+  position: relative;
114
+  padding: 0;
115
+  pointer-events: all;
116
+  cursor: pointer;
117
+
118
+  li {
119
+    display: inline-block;
120
+    padding: 0 .6em;
121
+  }
122
+
123
+  a {
124
+    @extend .link;
125
+  }
126
+}
127
+
128
+.post-list {
129
+  background-color: lighten($light-gray-color, 17%);
130
+  padding: $base-spacing 0;
131
+}
132
+
133
+.post-item {
134
+  padding-bottom: $small-spacing;
135
+  margin-bottom: $base-spacing;
136
+  border-bottom: 1px solid lighten($light-gray-color, 5%);
137
+
138
+  strong {
139
+    font-weight: 700;
140
+  }
141
+
142
+  &:last-child {
143
+    border-bottom: 0;
144
+    margin-bottom: 0;
145
+  }
146
+
147
+  .readmore {
148
+    font-style: italic;
149
+  }
150
+}
151
+
152
+.post-meta {
153
+  color: $medium-gray-color;
154
+  font-style: italic;
155
+}
156
+
157
+.post-link,
158
+.post a,
159
+.page a {
160
+  @extend .link;
161
+}
162
+
163
+.post {
164
+  @extend .clearfix;
165
+}
166
+
167
+.pagination {
168
+  li, a, span {
169
+    display: inline-block;
170
+  }
171
+
172
+  a, span {
173
+    font-size: rem(12);
174
+    padding: .5em;
175
+  }
176
+
177
+  .prev, .next {
178
+    @extend .link;
179
+  }
180
+}
181
+
182
+.share {
183
+  padding: $base-spacing 0 0;
184
+
185
+  @extend .pull-right;
186
+
187
+  h4 {
188
+    display: inline-block;
189
+  }
190
+}
191
+
192
+.post-responses {
193
+  background-color: lighten($light-gray-color, 16%);
194
+  padding: $base-spacing 0;
195
+
196
+  hr {
197
+    margin: $base-spacing 0;
198
+  }
199
+}
200
+
201
+.site-footer {
202
+  @extend .clearfix;
203
+
204
+  padding: $base-spacing 0;
205
+
206
+  a {
207
+    @extend .link;
208
+  }
209
+
210
+  small {
211
+    display: block;
212
+    font-size: rem(12);
213
+    color: darken($medium-gray-color, 10%);
214
+  }
215
+}

+ 96
- 0
priv/themes/simplest/src/_syntax-highlighting.scss View File

@@ -0,0 +1,96 @@
1
+/**
2
+* Syntax highlighting styles
3
+*/
4
+.highlight {
5
+  margin: $base-spacing 0;
6
+  padding: 0;
7
+  box-shadow: 0px 0px 2px rgba($black-color, .1);
8
+
9
+  .highlighter-rouge &, &, .hll, pre, code {
10
+    background-color: lighten($light-gray-color, 13%) !important;
11
+  }
12
+
13
+  pre {
14
+    margin: 0;
15
+    padding: $base-spacing;
16
+    white-space: pre;
17
+    line-height: 23px;
18
+    overflow-x: auto;
19
+    margin-bottom: 0;
20
+    word-break: inherit;
21
+    word-wrap: inherit;
22
+
23
+    &, code {
24
+      color: $base-font-color;
25
+    }
26
+
27
+    code {
28
+      white-space: pre;
29
+      padding: 0 !important;
30
+
31
+      * {
32
+        white-space: nowrap; // this sets all children inside to nowrap
33
+      }
34
+    }
35
+  }
36
+
37
+  .c     { color: #998; font-style: italic } // Comment
38
+  .err   { color: #a61717; background-color: #e3d2d2 } // Error
39
+  .k     { font-weight: bold } // Keyword
40
+  .o     { font-weight: bold } // Operator
41
+  .cm    { color: #998; font-style: italic } // Comment.Multiline
42
+  .cp    { color: #999; font-weight: bold } // Comment.Preproc
43
+  .c1    { color: #998; font-style: italic } // Comment.Single
44
+  .cs    { color: #999; font-weight: bold; font-style: italic } // Comment.Special
45
+  .gd    { color: #000; background-color: #fdd } // Generic.Deleted
46
+  .gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
47
+  .ge    { font-style: italic } // Generic.Emph
48
+  .gr    { color: #a00 } // Generic.Error
49
+  .gh    { color: #999 } // Generic.Heading
50
+  .gi    { color: #000; background-color: #dfd } // Generic.Inserted
51
+  .gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
52
+  .go    { color: #888 } // Generic.Output
53
+  .gp    { color: #555 } // Generic.Prompt
54
+  .gs    { font-weight: bold } // Generic.Strong
55
+  .gu    { color: #aaa } // Generic.Subheading
56
+  .gt    { color: #a00 } // Generic.Traceback
57
+  .kc    { font-weight: bold } // Keyword.Constant
58
+  .kd    { font-weight: bold } // Keyword.Declaration
59
+  .kp    { font-weight: bold } // Keyword.Pseudo
60
+  .kr    { font-weight: bold } // Keyword.Reserved
61
+  .kt    { color: #458; font-weight: bold } // Keyword.Type
62
+  .m     { color: #099 } // Literal.Number
63
+  .s     { color: #d14 } // Literal.String
64
+  .na    { color: #008080 } // Name.Attribute
65
+  .nb    { color: #0086B3 } // Name.Builtin
66
+  .nc    { color: #458; font-weight: bold } // Name.Class
67
+  .no    { color: #008080 } // Name.Constant
68
+  .ni    { color: #800080 } // Name.Entity
69
+  .ne    { color: #900; font-weight: bold } // Name.Exception
70
+  .nf    { color: #900; font-weight: bold } // Name.Function
71
+  .nn    { color: #555 } // Name.Namespace
72
+  .nt    { color: #000080 } // Name.Tag
73
+  .nv    { color: #008080 } // Name.Variable
74
+  .ow    { font-weight: bold } // Operator.Word
75
+  .w     { color: #bbb } // Text.Whitespace
76
+  .mf    { color: #099 } // Literal.Number.Float
77
+  .mh    { color: #099 } // Literal.Number.Hex
78
+  .mi    { color: #099 } // Literal.Number.Integer
79
+  .mo    { color: #099 } // Literal.Number.Oct
80
+  .sb    { color: #d14 } // Literal.String.Backtick
81
+  .sc    { color: #d14 } // Literal.String.Char
82
+  .sd    { color: #d14 } // Literal.String.Doc
83
+  .s2    { color: #d14 } // Literal.String.Double
84
+  .se    { color: #d14 } // Literal.String.Escape
85
+  .sh    { color: #d14 } // Literal.String.Heredoc
86
+  .si    { color: #d14 } // Literal.String.Interpol
87
+  .sx    { color: #d14 } // Literal.String.Other
88
+  .sr    { color: #009926 } // Literal.String.Regex
89
+  .s1    { color: #d14 } // Literal.String.Single
90
+  .ss    { color: #990073 } // Literal.String.Symbol
91
+  .bp    { color: #999 } // Name.Builtin.Pseudo
92
+  .vc    { color: #008080 } // Name.Variable.Class
93
+  .vg    { color: #008080 } // Name.Variable.Global
94
+  .vi    { color: #008080 } // Name.Variable.Instance
95
+  .il    { color: #099 } // Literal.Number.Integer.Long
96
+}

+ 46
- 0
priv/themes/simplest/src/_variables.scss View File

@@ -0,0 +1,46 @@
1
+// Typography
2
+$base-font-family: 'Open Sans', 'Lato', sans-serif;
3
+// $base-font-family: 'Merriweather', "Georgia", serif;
4
+$heading-font-family: 'Merriweather', $base-font-family;
5
+
6
+// Font Sizes
7
+$base-font-size: 1em;
8
+
9
+// Line height
10
+$base-line-height: 1.5;
11
+$heading-line-height: 1.2;
12
+
13
+// Other Sizes
14
+$base-border-radius: .52em;
15
+$base-spacing: $base-line-height * 1em;
16
+$small-spacing: $base-spacing / 2;
17
+$base-z-index: 0;
18
+
19
+// Colors
20
+$primary-color: #ec2028;
21
+$action-color: $primary-color;
22
+$white-color: #fff;
23
+$black-color: #000;
24
+$blue-color: #477dca;
25
+$dark-gray-color: #333;
26
+$medium-gray-color: #999;
27
+$light-gray-color: #ccc;
28
+
29
+// Font Colors
30
+$base-font-color: rgba($dark-gray-color, .8);
31
+
32
+// Border
33
+$base-border-color: $light-gray-color;
34
+$base-border: 1px solid $base-border-color;
35
+
36
+// Background Colors
37
+$base-background-color: $white-color;
38
+$secondary-background-color: tint($base-border-color, 80%);
39
+
40
+// Forms
41
+$form-box-shadow: inset 0 1px 3px rgba($black-color, 0.06);
42
+$form-box-shadow-focus: $form-box-shadow, 0 0 5px adjust-color($action-color, $lightness: -5%, $alpha: -0.3);
43
+
44
+// Animations
45
+$base-duration: 150ms;
46
+$base-timing: ease;

+ 11
- 0
priv/themes/simplest/src/base/_base.scss View File

@@ -0,0 +1,11 @@
1
+// Bitters 1.1.0
2
+// http://bitters.bourbon.io
3
+// Copyright 2013-2015 thoughtbot, inc.
4
+// MIT License
5
+
6
+@import "reset";
7
+@import "buttons";
8
+@import "forms";
9
+@import "lists";
10
+@import "tables";
11
+@import "typography";

+ 37
- 0
priv/themes/simplest/src/base/_buttons.scss View File

@@ -0,0 +1,37 @@
1
+// #{$all-buttons} {
2
+
3
+.button {
4
+  appearance: none;
5
+  background-color: $action-color;
6
+  border: 0;
7
+  border-radius: $base-border-radius;
8
+  color: $white-color;
9
+  cursor: pointer;
10
+  display: inline-block;
11
+  font-family: $base-font-family;
12
+  font-size: $base-font-size;
13
+  -webkit-font-smoothing: antialiased;
14
+  font-weight: 600;
15
+  line-height: 1;
16
+  padding: $small-spacing $base-spacing;
17
+  text-decoration: none;
18
+  transition: background-color $base-duration $base-timing;
19
+  user-select: none;
20
+  vertical-align: middle;
21
+  white-space: nowrap;
22
+
23
+  &:hover,
24
+  &:focus {
25
+    background-color: shade($action-color, 20%);
26
+    color: $white-color;
27
+  }
28
+
29
+  &:disabled {
30
+    cursor: not-allowed;
31
+    opacity: 0.5;
32
+
33
+    &:hover {
34
+      background-color: $action-color;
35
+    }
36
+  }
37
+}

+ 90
- 0
priv/themes/simplest/src/base/_forms.scss View File

@@ -0,0 +1,90 @@
1
+fieldset {
2
+  background-color: $secondary-background-color;
3
+  border: $base-border;
4
+  margin: 0 0 $small-spacing;
5
+  padding: $base-spacing;
6
+}
7
+
8
+input,
9
+label,
10
+select {
11
+  display: block;
12
+  font-family: $base-font-family;
13
+  font-size: $base-font-size;
14
+}
15
+
16
+label {
17
+  font-weight: 600;
18
+  margin-bottom: $small-spacing / 2;
19
+
20
+  &.required::after {
21
+    content: "*";
22
+  }
23
+
24
+  abbr {
25
+    display: none;
26
+  }
27
+}
28
+
29
+#{$all-text-inputs},
30
+select[multiple=multiple] {
31
+  background-color: $base-background-color;
32
+  border: $base-border;
33
+  border-radius: $base-border-radius;
34
+  box-shadow: $form-box-shadow;
35
+  box-sizing: border-box;
36
+  font-family: $base-font-family;
37
+  font-size: $base-font-size;
38
+  margin-bottom: $small-spacing;
39
+  padding: $base-spacing / 3;
40
+  transition: border-color $base-duration $base-timing;
41
+  width: 100%;
42
+
43
+  &:hover {
44
+    border-color: shade($base-border-color, 20%);
45
+  }
46
+
47
+  &:focus {
48
+    border-color: $action-color;
49
+    box-shadow: $form-box-shadow-focus;
50
+    outline: none;
51
+  }
52
+
53
+  &:disabled {
54
+    background-color: shade($base-background-color, 5%);
55
+    cursor: not-allowed;
56
+
57
+    &:hover {
58
+      border: $base-border;
59
+    }
60
+  }
61
+}
62
+
63
+textarea {
64
+  resize: vertical;
65
+}
66
+
67
+input[type="search"] {
68
+  appearance: none;
69
+}
70
+
71
+input[type="checkbox"],
72
+input[type="radio"] {
73
+  display: inline;
74
+  margin-right: $small-spacing / 2;
75
+
76
+  + label {
77
+    display: inline-block;
78
+  }
79
+}
80
+
81
+input[type="file"] {
82
+  margin-bottom: $small-spacing;
83
+  width: 100%;
84
+}
85
+
86
+select {
87
+  margin-bottom: $base-spacing;
88
+  max-width: 100%;
89
+  width: auto;
90
+}

+ 31
- 0
priv/themes/simplest/src/base/_lists.scss View File

@@ -0,0 +1,31 @@
1
+ul,
2
+ol {
3
+  list-style-type: none;
4
+  margin: 0;
5
+  padding: 0;
6
+
7
+  &%default-ul {
8
+    list-style-type: disc;
9
+    margin-bottom: $small-spacing;
10
+    padding-left: $base-spacing;
11
+  }
12
+
13
+  &%default-ol {
14
+    list-style-type: decimal;
15
+    margin-bottom: $small-spacing;
16
+    padding-left: $base-spacing;
17
+  }
18
+}
19
+
20
+dl {
21
+  margin-bottom: $small-spacing;
22
+
23
+  dt {
24
+    font-weight: bold;
25
+    margin-top: $small-spacing;
26
+  }
27
+
28
+  dd {
29
+    margin: 0;
30
+  }
31
+}

+ 54
- 0
priv/themes/simplest/src/base/_reset.scss View File

@@ -0,0 +1,54 @@
1
+/* http://meyerweb.com/eric/tools/css/reset/
2
+   v2.0 | 20110126
3
+   License: none (public domain)
4
+*/
5
+
6
+html, body, div, span, applet, object, iframe,
7
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8
+a, abbr, acronym, address, big, cite, code,
9
+del, dfn, em, img, ins, kbd, q, s, samp,
10
+small, strike, strong, sub, sup, tt, var,
11
+b, u, i, center,
12
+dl, dt, dd, ol, ul, li,
13
+fieldset, form, label, legend,
14
+table, caption, tbody, tfoot, thead, tr, th, td,
15
+article, aside, canvas, details, embed,
16
+figure, figcaption, footer, header, hgroup,
17
+menu, nav, output, ruby, section, summary,
18
+time, mark, audio, video {
19
+  margin: 0;
20
+  padding: 0;
21
+  border: 0;
22
+  font-size: 100%;
23
+  font: inherit;
24
+  vertical-align: baseline;
25
+}
26
+
27
+/* HTML5 display-role reset for older browsers */
28
+article, aside, details, figcaption, figure,
29
+footer, header, hgroup, menu, nav, section {
30
+  display: block;
31
+}
32
+
33
+body {
34
+  line-height: 1;
35
+}
36
+
37
+ol, ul {
38
+  list-style: none;
39
+}
40
+
41
+blockquote, q {
42
+  quotes: none;
43
+}
44
+
45
+blockquote:before, blockquote:after,
46
+q:before, q:after {
47
+  content: '';
48
+  content: none;
49
+}
50
+
51
+table {
52
+  border-collapse: collapse;
53
+  border-spacing: 0;
54
+}

+ 25
- 0
priv/themes/simplest/src/base/_tables.scss View File

@@ -0,0 +1,25 @@
1
+table {
2
+  border-collapse: collapse;
3
+  font-feature-settings: "kern", "liga", "tnum";
4
+  margin: $small-spacing 0;
5
+  table-layout: fixed;
6
+  width: 100%;
7
+}
8
+
9
+th {
10
+  border-bottom: 1px solid shade($base-border-color, 25%);
11
+  font-weight: 600;
12
+  padding: $small-spacing 0;
13
+  text-align: left;
14
+}
15
+
16
+td {
17
+  border-bottom: $base-border;
18
+  padding: $small-spacing 0;
19
+}
20
+
21
+tr,
22
+td,
23
+th {
24
+  vertical-align: middle;
25
+}

+ 105
- 0
priv/themes/simplest/src/base/_typography.scss View File

@@ -0,0 +1,105 @@
1
+body {
2
+  color: $base-font-color;
3
+  font-family: $base-font-family;
4
+  font-feature-settings: "kern", "liga", "pnum";
5
+  font-size: $base-font-size;
6
+  line-height: $base-line-height;
7
+}
8
+
9
+h1,
10
+h2,
11
+h3,
12
+h4,
13
+h5,
14
+h6 {
15
+  font-family: $heading-font-family;
16
+  line-height: $heading-line-height;
17
+  margin: 0 0 $small-spacing;
18
+  font-weight: 300;
19
+}
20
+
21
+h1 {
22
+  font-size: 1.875rem;
23
+}
24
+
25
+h2 {
26
+  font-size: 1.75em;
27
+}
28
+
29
+h3 {
30
+  font-size: 1.5rem;
31
+}
32
+
33
+h4 {
34
+  font-size: 1.25rem;
35
+}
36
+
37
+h5,
38
+h6 {
39
+  font-size: 1.125rem;
40
+}
41
+
42
+p {
43
+  margin: 0 0 $small-spacing;
44
+
45
+  &.lead {
46
+    font-size: rem(22);
47
+    font-weight: 300;
48
+  }
49
+}
50
+
51
+a {
52
+  color: $action-color;
53
+  text-decoration: none;
54
+  transition: color $base-duration $base-timing;
55
+
56
+  &:active,
57
+  &:focus,
58
+  &:hover {
59
+    color: shade($action-color, 25%);
60
+  }
61
+}
62
+
63
+hr {
64
+  border-bottom: $base-border;
65
+  border-left: 0;
66
+  border-right: 0;
67
+  border-top: 0;
68
+  margin: $base-spacing 0;
69
+}
70
+
71
+img,
72
+picture {
73
+  margin: 0;
74
+  max-width: 100%;
75
+}
76
+
77
+blockquote {
78
+  padding: 0 0 0 $base-spacing;
79
+  margin: $base-spacing 0;
80
+  color: $medium-gray-color;
81
+  line-height: 1.8;
82
+  border-left: $small-spacing solid $light-gray-color;
83
+}
84
+
85
+code {
86
+  background: none;
87
+  border-radius: 0;
88
+  border: none;
89
+  font-family: "Courier New", monospace;
90
+  font-size: 0.9em;
91
+  margin: 0;
92
+  padding: 0 0.5em;
93
+  background-color: lighten($light-gray-color, 13%);
94
+}
95
+
96
+pre {
97
+  -webkit-overflow-scrolling: touch;
98
+  font-family: "Courier New", monospace;
99
+  font-size: 0.9em;
100
+  margin: 0;
101
+
102
+  code {
103
+    line-height: 1.75em;
104
+  }
105
+}

+ 8
- 0
priv/themes/simplest/src/main.scss View File

@@ -0,0 +1,8 @@
1
+
2
+@import "bourbon";
3
+
4
+@import "variables";
5
+@import "base/base";
6
+
7
+@import "layout";
8
+@import "syntax-highlighting";

+ 11
- 0
priv/themes/simplest/tmpl/_footer.html.liquid View File

@@ -0,0 +1,11 @@
1
+      <footer class="site-footer">
2
+        <div class="container">
3
+          <small class="pull-right">by <a href="{{ h.card.uri }}"
4
+                                          title="{{ h.card.displayed_name }}"
5
+                                          target="_blank">{{ h.card.displayed_name }}</a>
6
+          </small>
7
+        </div>
8
+      </footer>
9
+    </main>
10
+  </body>
11
+</html>

+ 29
- 0
priv/themes/simplest/tmpl/_header.html.liquid View File

@@ -0,0 +1,29 @@
1
+<!DOCTYPE html>
2
+<html>
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta name="apple-mobile-web-app-capable" content="yes">
6
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+    <meta name="viewport" content="width=device-width, initial-scale=1">
8
+    <meta name="HandheldFriendly" content="True">
9
+    <meta name="MobileOptimized" content="320">
10
+    {% koype.render meta %}
11
+    {% import type=style path=theme.css %}
12
+  </head>
13
+  <body>
14
+    <main class="wrapper">
15
+      <header class="site-header">
16
+        <nav class="nav">
17
+          <div class="container">
18
+            <h1 class="logo h-card"><a class="p-name u-url" href="{{ '/' | prepend: h.card.uri }}"><span>{{ h.card.displayed_name }}</span></a></h1>
19
+            <ul class="navbar">
20
+              {% if signed_in? %}
21
+                <li><a rel="nofllow" href="{% route settings.view %}">settings</a></li>
22
+                <li><a rel="nofllow" href="{% route auth.logout %}">logout</a></li>
23
+              {% else %}
24
+                <li><a rel="nofollow" href="{% route auth.login %}">login</a></li>
25
+              {% endif %}
26
+            </ul>
27
+          </div>
28
+        </nav>
29
+      </header>

+ 17
- 0
priv/themes/simplest/tmpl/entry/_comments.html.liquid View File

@@ -0,0 +1,17 @@
1
+{% assign comments = webmention.of.reply %}
2
+{% assign comment_count = comments|size %}
3
+<ul class="list pa0 ma0">
4
+  {% for comment in comments %}
5
+    <li class="pa2 h-cite p-comment">
6
+      <a class="link u-author h-card" href="{{ comment.author.url }}" title="{{ comment.author.name }}">
7
+        <img class="br-100 " src="{{ comment.author.photo }}" alt="{{ comment.author.name }}" />
8
+      </a>
9
+      <aside>
10
+        <p class="e-content">{{ comment.source.content.html }}</p>
11
+        <a class="u-url" href="{{ comment.url }}">
12
+          posted <time class="dt-published underline" datetime="{{ comment.source.published }}">{{ comment.source.published }}</time>
13
+        </a>
14
+      </aside>
15
+    </li>
16
+  {% endfor %}
17
+</ul>

+ 17
- 0
priv/themes/simplest/tmpl/entry/_facepile.html.liquid View File

@@ -0,0 +1,17 @@
1
+{% assign faces = webmention.of|map_get:facepile %}
2
+{% assign count_of_faces = faces|size %}
3
+{% if faces != empty %}
4
+<details id="mentions_{{ facepile }}" data-reply-count={{ count_of_faces }}>
5
+  <summary>Received {{ facepile|inflect:count_faces}} {{ faces|size }}</summary>
6
+  <ul class="dib list">
7
+    {% for face in faces %}
8
+    <li class="dib pa1 ma1 h-cite p-{{ facepile }}">
9
+      <a class="link u-author h-card" href="{{ face.author.url }}" title="{{ face.author.name }}">
10
+        <img class="br-100 " src="{{ face.author.photo }}" alt="{{ face.author.name }}" />
11
+      </a>
12
+      <a href="{{ face.url }}" class="u-url dn"></a>
13
+    </li>
14
+    {% endfor %}
15
+  </ul>
16
+</details>
17
+{% endif %}

+ 23
- 0
priv/themes/simplest/tmpl/entry/_mentions.html.liquid View File

@@ -0,0 +1,23 @@
1
+{% assign other_count = webmention.of.other|size %}
2
+{% if other_count == 0 %}
3
+{% else %}
4
+  <ul>
5
+    {% for mention in webmention.of.other %}
6
+      <li class="u-mention h-cite">
7
+        <a class="h-card u-author v-mid" href="{{ mention.author.url }}">
8
+          <img src="{{ mention.author.photo }}" alt="" class="u-photo" />
9
+          <span class="p-name">{{ mention.author.name }}</span>
10
+        </a>
11
+        <div>
12
+          <p class="p-name">{{ mention.source.title }}</p>
13
+          <a class="u-url u-uid" href="{{ mention.url }}">
14
+            posted
15
+            <time datetime="{{ mention.source.published }}" class="dt-published">{{ mention.source.published }}</time>
16
+            ; fetched
17
+            <time datetime="{{ mention.accessed }}" class="dt-accessed">{{ mention.accessed }}</time>
18
+          </a>
19
+        </div>
20
+      </li>
21
+    {% endfor %}
22
+  </ul>
23
+{% endif %}

+ 5
- 0
priv/themes/simplest/tmpl/entry/_reply-body.html.liquid View File

@@ -0,0 +1,5 @@
1
+<aside class="h-cite {{ reaction_class }}">
2
+  {{ prefix_name }} <a href="{{ reaction.author.url }}" class="u-author">{{
3
+    reaction.author.name }}</a>'s post &mdash;
4
+  <a href="{{ reaction.url }}" class="u-url"></a>
5
+</aside>

+ 25
- 0
priv/themes/simplest/tmpl/entry/_reply.html.liquid View File

@@ -0,0 +1,25 @@
1
+{% for reaction in entry.json["bookmark-of"] %}
2
+  {% assign prefix_name = "Bookmarked" %}
3
+  {% assign reaction_class = "u-bookmark-of" %}
4
+  {% render name=entry/_reply-body.html %}
5
+{% endfor %}
6
+{% for reaction in entry.json["repost-of"] %}
7
+  {% assign prefix_name = "Reposted" %}
8
+  {% assign reaction_class = "u-repost-of" %}
9
+  {% render name=entry/_reply-body.html %}
10
+{% endfor %}
11
+{% for reaction in entry.json["like-of"] %}
12
+  {% assign prefix_name = "Like" %}
13
+  {% assign reaction_class = "u-like-of" %}
14
+  {% render name=entry/_reply-body.html %}
15
+{% endfor %}
16
+{% for reaction in entry.json["in-reply-to"] %}
17
+  {% assign prefix_name = "Replied to" %}
18
+  {% assign reaction_class = "u-in-reply-to" %}
19
+  {% render name=entry/_reply-body.html %}
20
+{% endfor %}
21
+{% for reaction in entry.json["checkin"] %}
22
+  {% assign prefix_name = "Checked into" %}
23
+  {% assign reaction_class = "u-checkin" %}
24
+  {% render name=entry/_reply-body.html %}
25
+{% endfor %}

+ 13
- 0
priv/themes/simplest/tmpl/entry/gone.html.liquid View File

@@ -0,0 +1,13 @@
1
+<article class="h-entry post container" itemscope itemtype="http://schema.org/BlogPosting">
2
+  <header class="post-header">
3
+    <h1 class="p-name post-title" itemprop="name headline">Entry Not Found</h1>
4
+    <p class="post-meta">
5
+      <a href="{{h.card.uri}}" class="p-author h-card" itemprop="author" itemscope itemtype="http://schema.org/Person">
6
+        <span class="p-name" itemprop="name">{{ h.card.displayed_name }}</span>
7
+      </a>
8
+    </p>
9
+  </header>
10
+  <main class="e-content post-content" itemprop="articleBody">
11
+    The specified post is gone.
12
+  </main>
13
+</article>

+ 13
- 0
priv/themes/simplest/tmpl/entry/not-found.html.liquid View File

@@ -0,0 +1,13 @@
1
+<article class="h-entry post container" itemscope itemtype="http://schema.org/BlogPosting">
2
+  <header class="post-header">
3
+    <h1 class="p-name post-title" itemprop="name headline">Entry Not Found</h1>
4
+    <p class="post-meta">
5
+      <a href="{{h.card.uri}}" class="p-author h-card" itemprop="author" itemscope itemtype="http://schema.org/Person">
6
+        <span class="p-name" itemprop="name">{{ h.card.displayed_name }}</span>
7
+      </a>
8
+    </p>
9
+  </header>
10
+  <main class="e-content post-content" itemprop="articleBody">
11
+    The specified post was not found.
12
+  </main>
13
+</article>

+ 60
- 0
priv/themes/simplest/tmpl/entry/view.html.liquid View File

@@ -0,0 +1,60 @@
1
+<div class="h-entry">
2
+<article class="post container" itemscope itemtype="http://schema.org/BlogPosting">
3
+  <header class="post-header">
4
+    {% if entry.name %}<h1 class="e-name post-title" itemprop="name headline">{{ entry.name }}</h1>{% endif %}
5
+    <p class="post-meta">
6
+      <a class="u-url" href="{{ entry.uri }}"><time class="dt-published"
7
+          datetime="{{ entry.published_at | date_to_xmlschema }}"
8
+          itemprop="datePublished">{{ entry.published_at | date: "%b %-d, %Y"
9
+          }}</time></a>
10
+      • by
11
+      <a href="{{h.card.uri}}" class="p-author h-card" itemprop="author" itemscope itemtype="http://schema.org/Person">
12
+        <span class="p-name" itemprop="name">{{ h.card.displayed_name }}</span>
13
+      </a>
14
+    </p>
15
+  </header>
16
+  {% render name=entry/_reply.html %}
17
+  {% assign content = entry.json.content %}
18
+  {% assign text_content = content.text %}
19
+  {% assign html_content = content.html %}
20
+  {% if html_content != nil %}
21
+    <main class="e-content {% unless entry.name %}e-name {% endunless %}post-content" itemprop="articleBody">
22
+    {{ html_content }}
23
+    </main>
24
+  {% elsif text_content != nil %}
25
+    <main class="p-content {% unless entry.name %}p-name {% endunless %}post-content" itemprop="articleBody">
26
+    {{ text_content }}
27
+    </main>
28
+  {% endif %}
29
+</article>
30
+<aside class="post-responses">
31
+  <div class="container">
32
+    <aside class="order-3 order-2-l self-start w-auto flex-auto flex-grow ma0-l pt3 pt0-l measure-l">
33
+      {% render name=entry/_facepile.html facepile=like %}
34
+      {% render name=entry/_facepile.html facepile=repost %}
35
+      {% render name=entry/_facepile.html facepile=bookmark %}
36
+    </aside>
37
+    <h3>Responses</h3>
38
+    {% render name=entry/_comments.html %}
39
+    <aside class="mw-100 self-center ma0-ns order-1-l order-2 dark-gray flex-auto flex-grow">
40
+      <p>
41
+        Want to reply to this {{ entry.type }} natively?
42
+        <a target="_new" href="https://indieweb.org/reply#How_To" class="link underline navy fw7">Learn how</a>.
43
+      </p>
44
+      <form method="post" action="{% route indie_webmention.incoming %}">
45
+        <input type=hidden name=target value="{{ h.card.uri }}{{ entry.uri }}" />
46
+        <input type=hidden name=_format value="html" />
47
+        <label for="source">Enter your response URI below!</label>
48
+        <input name="source" placeholder="https://yoursite.here/response" type="url" id="manualWebmentionId">
49
+        <button>Send</button>
50
+      </form>
51
+      <p>
52
+        You can leave an
53
+        <a href="https://quill.p3k.io/?dontask=1&me=https://commentpara.de&reply={% route current %}">
54
+          anonymonous comment</a>, thanks to <a href="https://commentpara.de/">Comment Parade</a>.
55
+      </p>
56
+    </aside>
57
+    {% render name=entry/_mentions.html %}
58
+  </div>
59
+</aside>
60
+</div>

+ 1
- 0
priv/themes/simplest/tmpl/follow.html.liquid View File

@@ -0,0 +1 @@
1
+

+ 29
- 0
priv/themes/simplest/tmpl/home.html.liquid View File

@@ -0,0 +1,29 @@
1
+<section class="intro">
2
+  <div class="container">
3
+    <p class="h-card">
4
+      Hi, I'm <a class="u-url" href="{{ h.card.uri }}"><strong class="p-name author-name" itemprop="name">{{ h.card.displayed_name }}</strong></a>.
5
+      <span class="p-note">{{ h.card.note }}</span>
6
+  </div>
7
+</section>
8
+
9
+<section class="post-list">
10
+  <div class="container">
11
+    <h2>Latest Entry</h2>
12
+    {% assign entry = "latest"|get_entry %}
13
+    <article class="post-item h-entry">
14
+      <p>
15
+        <time class="dt-published post-meta">{{ entry.published_at | date: "%b %-d, %Y" }}</time>
16
+        {% if entry.name %}&mdash; <strong><a class="p-name post-link" href="{{ entry.uri }}">{{ entry.name }}</a></strong>{% endif %}
17
+        &mdash;
18
+        <span class="p-content">{{ entry.json.content.plain | truncatewords: 50 }}</span>
19
+        &mdash;
20
+        <a class="u-url post-link readmore" href="{{ entry.uri }}">Read more</a>
21
+      </p>
22
+    </article>
23
+    <p>
24
+      <a href="{% route stream %}">More Entries</a>
25
+      &mdash;
26
+      <a href="{% route feed.index %}">Subscribe</a>
27
+    </P>
28
+  </div>
29
+</section>

+ 18
- 0
priv/themes/simplest/tmpl/stream/empty.html.liquid View File

@@ -0,0 +1,18 @@
1
+<main class="h-feed">
2
+  <section class="intro">
3
+    <div class="container">
4
+      <h3>
5
+        <a class="p-name u-url" href="{% route stream %}">Stream</a>
6
+      </h3>
7
+      <p>
8
+        Hi, I'm <a class="u-author" href="{{ h.card.uri }}"><strong class="author-name" itemprop="name">{{ h.card.displayed_name }}</strong></a>.
9
+        <span class="p-note">{{ h.card.note }}</span>
10
+      </p>
11
+    </div>
12
+  </section>
13
+  <section class="post-list">
14
+    <div class="container">
15
+      <p>There's nothing to show!</p>
16
+    </div>
17
+  </section>
18
+</main>

+ 28
- 0
priv/themes/simplest/tmpl/stream/page.html.liquid View File

@@ -0,0 +1,28 @@
1
+<main class="h-feed">
2
+  <section class="intro">
3
+    <div class="container">
4
+      <h3>
5
+        <a class="p-name u-url" href="{% route stream %}">Stream</a>
6
+      </h3>
7
+      <p>
8
+        Hi, I'm <a class="u-author" href="{{ h.card.uri }}"><strong class="author-name" itemprop="name">{{ h.card.displayed_name }}</strong></a>.
9
+        <span class="p-note">{{ h.card.note }}</span>
10
+      </p>
11
+    </div>
12
+  </section>
13
+  <section class="post-list">
14
+    <div class="container">
15
+      {% for entry in paginator.entries %}
16
+        <article class="h-entry post-item">
17
+          <p>
18
+            <time class="dt-published post-meta">{{ entry.published_at | date: "%b %-d, %Y" }}</time>
19
+            {% if entry.name %}&mdash; <strong><a class="p-name post-link" href="{{ entry.uri }}">{{ entry.name }}</a></strong>{% endif %}
20
+            <span class="p-content">{{ entry.json.content.plain | truncatewords: 50 }}</span>
21
+            &mdash;
22
+            <a class="u-url post-link readmore" href="{{ entry.uri }}">Read more</a>
23
+          </p>
24
+        </article>
25
+      {% endfor %}
26
+    </div>
27
+  </section>
28
+</main>

+ 68
- 139
test/integration/template_test.exs View File

@@ -12,6 +12,17 @@ defmodule Koype.Integration.TemplateTest do
12 12
   @inbuilt_themes ~w(simplest)s
13 13
   @test_mf2_url "https://v2.jacky.wtf/post/fc7df928-fbcd-43ea-8c7f-5f99fef674be"
14 14
 
15
+  def assert_template_used(resp, template_name) do
16
+    assert {"x-koype-theme", template_name} in resp.resp_headers
17
+  end
18
+
19
+  def fetch_current_entry(html, url, type \\ "entry") do
20
+    assert mf2 = Microformats2.parse(html, url)
21
+    entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-#{type}" in entry[:type] end)
22
+    refute is_nil(entry_mf2)
23
+    entry_mf2
24
+  end
25
+
15 26
   for template_name <- ~w(default)s ++ @inbuilt_themes do
16 27
     describe template_name <> " - renders as expected" do
17 28
       test "200 ['home'] renders the homepage" do
@@ -22,7 +33,7 @@ defmodule Koype.Integration.TemplateTest do
22 33
                  :ok
23 34
                )
24 35
 
25
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
36
+        assert_template_used(resp, unquote(template_name))
26 37
       end
27 38
 
28 39
       test "200 ['follow'] renders the following page" do
@@ -51,7 +62,7 @@ defmodule Koype.Integration.TemplateTest do
51 62
 
52 63
         resp = build_conn() |> template(unquote(template_name)) |> get("/stream")
53 64
 
54
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
65
+        assert_template_used(resp, unquote(template_name))
55 66
 
56 67
         assert html =
57 68
                  html_response(
@@ -59,24 +70,22 @@ defmodule Koype.Integration.TemplateTest do
59 70
                    :ok
60 71
                  )
61 72
 
62
-        assert mf2 =
63
-                 Microformats2.parse(html, Koype.Web.Router.Helpers.timeline_url(resp, :sequential))
73
+        feed_url = Koype.Web.Router.Helpers.timeline_url(resp, :sequential)
74
+        feed_mf2 = fetch_current_entry(html, feed_url, "feed")
64 75
 
65
-        [h_feed, _card] = mf2[:items]
66
-
67
-        assert entries = h_feed[:children]
76
+        assert entries = feed_mf2[:children]
68 77
         assert length(entries) == 6
69 78
       end
70 79
 
71
-      test "200 stream/empty" do
80
+      test "200 ['stream/empty'] renders an empty stream" do
72 81
         resp = build_conn() |> template(unquote(template_name)) |> get("/stream")
82
+        assert_template_used(resp, unquote(template_name))
73 83
 
74
-        assert html_response(
75
-                 resp,
76
-                 :ok
77
-               )
84
+        assert html = html_response(resp, :ok)
78 85
 
79
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
86
+        feed_url = Koype.Web.Router.Helpers.timeline_url(resp, :sequential)
87
+        feed_mf2 = fetch_current_entry(html, feed_url, "feed") || %{children: nil}
88
+        refute feed_mf2[:children]
80 89
       end
81 90
 
82 91
       for {post_type, property, json_property} <- [
@@ -85,62 +94,7 @@ defmodule Koype.Integration.TemplateTest do
85 94
             {:reply, :in_reply_to, "in-reply-to"},
86 95
             {:bookmark, :bookmark_of, "bookmark-of"}
87 96
           ] do
88
-        test "200 entry/view - shows reply header for #{post_type} with HTML content" do
89
-          entry = insert(:entry, type: Atom.to_string(unquote(post_type)))
90
-
91
-          entry_json =
92
-            build(:entry_json)
93
-            |> with_html_content
94
-            |> Map.put_new_lazy(unquote(json_property), fn ->
95
-              Map.merge(
96
-                %{}
97
-                |> with_post_type(unquote(post_type))
98
-                |> Map.get(unquote(json_property))
99
-                |> List.first(),
100
-                %{"content" => %{"html" => "This is html.", "text" => "This is text."}}
101
-              )
102
-              |> List.wrap()
103
-            end)
104
-
105
-          {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
106
-          entry_url = Koype.Repo.Entry.get_uri(entry)
107
-
108
-          resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
109
-
110
-          assert html =
111
-                   html_response(
112
-                     resp,
113
-                     :ok
114
-                   )
115
-
116
-          assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
117
-          assert mf2 = Microformats2.parse(html, entry_url)
118
-          assert entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
119
-          assert response_json = entry_json[unquote(json_property)] |> List.first()
120
-          assert response_mf2 = entry_mf2[:properties][unquote(property)] |> List.first()
121
-          assert response_author = response_mf2[:properties][:author] |> List.first()
122
-
123
-          for key <- ~w(url name) do
124
-            assert response_json["author"][key] in Map.get(
125
-                     response_author[:properties],
126
-                     String.to_atom(key),
127
-                     []
128
-                   ),
129
-                   "Failed to find the #{key} for the loaded MF2."
130
-          end
131
-
132
-          assert response_json["url"] in response_mf2[:properties][:url]
133
-
134
-          assert response_json["content"]["html"] ==
135
-                   response_mf2[:properties][:content] |> List.first() |> Map.get(:html)
136
-
137
-          assert response_json["content"]["html"] ==
138
-                   response_mf2[:properties][:content] |> List.first() |> Map.get(:text)
139
-
140
-          assert "h-cite" in response_mf2[:type]
141
-        end
142
-
143
-        test "200 entry/view - shows reply header for #{post_type} with only text content" do
97
+        test "200 entry/view - shows reply header for #{post_type}" do
144 98
           entry = insert(:entry, type: Atom.to_string(unquote(post_type)))
145 99
 
146 100
           entry_json =
@@ -172,32 +126,16 @@ defmodule Koype.Integration.TemplateTest do
172 126
           assert entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
173 127
           assert response_json = entry_json[unquote(json_property)] |> List.first()
174 128
           assert response_mf2 = entry_mf2[:properties][unquote(property)] |> List.first()
175
-          assert response_author = response_mf2[:properties][:author] |> List.first()
176
-
177
-          for key <- ~w(url name) do
178
-            assert response_json["author"][key] in Map.get(
179
-                     response_author[:properties],
180
-                     String.to_atom(key),
181
-                     []
182
-                   ),
183
-                   "Failed to find the #{key} for the loaded MF2."
184
-          end
129
+          assert response_author = response_mf2[:properties][:author]
130
+          assert response_json["author"]["url"] in response_author
185 131
 
186 132
           assert response_json["url"] in response_mf2[:properties][:url]
187
-          assert response_mf2[:properties][:content]
188
-
189
-          assert response_json["content"]["text"] ==
190
-                   response_mf2[:properties][:content] |> List.first() |> Map.get(:html)
191
-
192
-          assert response_json["content"]["text"] ==
193
-                   response_mf2[:properties][:content] |> List.first() |> Map.get(:text)
194
-
195 133
           assert "h-cite" in response_mf2[:type]
196 134
         end
197 135
       end
198 136
 
199 137
       test "200 entry/view - shows baseline" do
200
-        entry = insert(:entry, name: "")
138
+        entry = insert(:entry, name: "", type: "note")
201 139
         entry_json = build(:entry_json) |> with_html_content
202 140
         {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
203 141
         entry_url = Koype.Repo.Entry.get_uri(entry)
@@ -221,8 +159,16 @@ defmodule Koype.Integration.TemplateTest do
221 159
         assert entry_url in entry_mf2[:properties][:url]
222 160
         assert Koype.Template.format!(entry.published_at) in entry_mf2[:properties][:published]
223 161
         mf2_name = entry_mf2[:properties][:name]
224
-        json_name = entry_json["content"]["plain"]
225
-        assert json_name in mf2_name
162
+        mf2_content = entry_mf2[:properties][:content]
163
+        json_content = entry_json["content"]
164
+
165
+        expected_mf2_content = %{
166
+          html: json_content["html"] |> List.first(),
167
+          text: json_content["plain"]
168
+        }
169
+
170
+        assert mf2_name
171
+        assert expected_mf2_content in mf2_content
226 172
       end
227 173
 
228 174
       test "200 entry/view - shows name for articles" do
@@ -238,14 +184,11 @@ defmodule Koype.Integration.TemplateTest do
238 184
                    :ok
239 185
                  )
240 186
 
241
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
242
-        assert mf2 = Microformats2.parse(html, entry_url)
243
-        entry_mf2 = Enum.find(mf2[:items], fn mf2_item -> "h-entry" in mf2_item[:type] end)
244
-
245
-        refute is_nil(entry_mf2)
187
+        assert_template_used(resp, unquote(template_name))
188
+        entry_mf2 = fetch_current_entry(html, entry_url)
246 189
 
247
-        assert entry_url in entry_mf2[:properties][:url]
248
-        assert entry.name == entry_mf2[:properties][:name] |> List.first() |> Map.get(:text)
190
+        assert %{html: entry.name, text: entry.name} ==
191
+                 entry_mf2[:properties][:name] |> List.first()
249 192
       end
250 193
 
251 194
       test "200 entry/view - shows reactions" do
@@ -290,27 +233,19 @@ defmodule Koype.Integration.TemplateTest do
290 233
                    :ok
291 234
                  )
292 235
 
293
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
294
-        assert mf2 = Microformats2.parse(html, entry_url)
295
-        entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
296
-
297
-        refute is_nil(entry_mf2)
236
+        assert_template_used(resp, unquote(template_name))
237
+        entry_mf2 = fetch_current_entry(html, entry_url)
298 238
 
299
-        assert entry_url in entry_mf2[:properties][:url]
300
-        assert Koype.Template.format!(entry.published_at) in entry_mf2[:properties][:published]
301
-        mf2_name = entry_mf2[:properties][:name]
302
-        json_name = entry_json["content"]["plain"]
303
-        assert json_name in mf2_name
304 239
         likes = Map.get(entry_mf2[:properties], :like, [])
305 240
         assert length(likes) == 3
306
-        assert Enum.all?(likes, fn like -> "Jacky Alciné" == like[:value] end)
307 241
 
308 242
         assert Enum.all?(likes, fn like ->
309
-                 like[:properties][:url] |> List.first() =~ @test_mf2_url
243
+                 assert "Jacky Alciné" == like[:value]
244
+                 assert like[:properties][:url] |> List.first() =~ @test_mf2_url
310 245
                end)
311 246
       end
312 247
 
313
-      test "200 entry/view - shows comments" do
248
+      test "200 ['entry/view'] shows comments" do
314 249
         entry = insert(:entry, name: "")
315 250
         entry_json = build(:entry_json) |> with_html_content
316 251
         {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
@@ -352,17 +287,10 @@ defmodule Koype.Integration.TemplateTest do
352 287
                    :ok
353 288
                  )
354 289
 
355
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
356
-        assert mf2 = Microformats2.parse(html, entry_url)
357
-        entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
358
-
359
-        refute is_nil(entry_mf2)
290
+        assert_template_used(resp, unquote(template_name))
291
+        entry_mf2 = fetch_current_entry(html, entry_url)
360 292
 
361
-        assert entry_url in entry_mf2[:properties][:url]
362 293
         assert Koype.Template.format!(entry.published_at) in entry_mf2[:properties][:published]
363
-        mf2_name = entry_mf2[:properties][:name]
364
-        json_name = entry_json["content"]["plain"]
365
-        assert json_name in mf2_name
366 294
         comments = Map.get(entry_mf2[:properties], :comment, [])
367 295
         assert length(comments) == 3
368 296
 
@@ -374,19 +302,21 @@ defmodule Koype.Integration.TemplateTest do
374 302
 
375 303
       test "200 entry/view - no content in entry" do
376 304
         entry = insert(:entry)
377
-        entry_json = build(:entry_json) |> Map.drop(~w(name content summary))
378
-        {:ok, _} = Koype.Repo.Entry.json_persist(entry, entry_json)
305
+
306
+        {:ok, entry_json} =
307
+          build(:entry_json)
308
+          |> Map.drop(~w(name content summary))
309
+          |> IndieWeb.Micropub.Content.process_properties()
310
+
311
+        Koype.Repo.Entry.json_persist(entry, entry_json)
379 312
         entry_url = Koype.Repo.Entry.get_uri(entry)
380 313
 
381 314
         resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
382 315
 
383 316
         assert html = html_response(resp, :ok)
384 317
 
385
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
386
-        assert mf2 = Microformats2.parse(html, entry_url)
387
-        entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
388
-
389
-        refute is_nil(entry_mf2)
318
+        assert_template_used(resp, unquote(template_name))
319
+        entry_mf2 = fetch_current_entry(html, entry_url)
390 320
         refute entry_mf2[:properties][:content]
391 321
       end
392 322
 
@@ -406,20 +336,21 @@ defmodule Koype.Integration.TemplateTest do
406 336
 
407 337
         assert html = html_response(resp, :ok)
408 338
 
409
-        assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
410
-        assert mf2 = Microformats2.parse(html, entry_url)
411
-        entry_mf2 = Enum.find(mf2[:items], fn entry -> "h-entry" in entry[:type] end)
339
+        assert_template_used(resp, unquote(template_name))
340
+        entry_mf2 = fetch_current_entry(html, entry_url)
412 341
 
413
-        refute is_nil(entry_mf2)
414
-
415
-        assert %{html: entry_json["content"]["html"], text: entry_json["content"]["plain"]} in entry_mf2[
416
-                 :properties
417
-               ][:content]
342
+        assert %{
343
+                 html: "<p>#{entry_json["content"]["plain"]}</p>",
344
+                 text: entry_json["content"]["plain"]
345
+               } in entry_mf2[:properties][:content]
418 346
       end
419 347
 
420 348
       test "410 entry/gone" do
421
-        {:ok, entry} = insert(:entry) |> Koype.Repo.Entry.delete()
422
-        Koype.Repo.Entry.json_persist(entry, build(:entry_json))
349
+        {:ok, entry} =
350
+          insert(:entry, name: Faker.Lorem.sentence())
351
+          |> Koype.Repo.Entry.delete()
352
+
353
+        {:ok, _} = Koype.Repo.Entry.json_persist(entry, build(:entry_json))
423 354
         entry_url = Koype.Repo.Entry.get_uri(entry)
424 355
         resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
425 356
 
@@ -430,11 +361,10 @@ defmodule Koype.Integration.TemplateTest do
430 361
                  )
431 362
 
432 363
         assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
433
-        assert html =~ "h-entry"
434 364
         refute html =~ entry.name
435 365
       end
436 366
 
437
-      test "entry/not-found" do
367
+      test "404 entry/not-found" do
438 368
         entry = insert(:entry, name: Faker.Lorem.sentence())
439 369
         entry_url = Koype.Repo.Entry.get_uri(entry)
440 370
         resp = build_conn() |> template(unquote(template_name)) |> get(entry_url)
@@ -447,7 +377,6 @@ defmodule Koype.Integration.TemplateTest do
447 377
 
448 378
         assert {"x-koype-theme", unquote(template_name)} in resp.resp_headers
449 379
         refute html =~ entry.name
450
-        assert html =~ "h-entry"
451 380
       end
452 381
     end
453 382
   end

Loading…
Cancel
Save