Compare commits
3 commits
47d255a3e8
...
661f6f4f51
| Author | SHA1 | Date | |
|---|---|---|---|
| 661f6f4f51 | |||
| d830e52b52 | |||
| 3f11c40acb |
3 changed files with 102 additions and 25 deletions
|
|
@ -28,26 +28,23 @@
|
||||||
|
|
||||||
h1, h2, h3 {
|
h1, h2, h3 {
|
||||||
font-family: serif;
|
font-family: serif;
|
||||||
margin: 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
line-height: 48px;
|
line-height: 48px;
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
clear: both;
|
clear: both;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
img,
|
img,
|
||||||
|
|
@ -80,6 +77,33 @@
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.tabs .tab-list {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.tabs .tab-list .tab {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.tabs .tab-list .tab:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.tabs .tab-list .tab a {
|
||||||
|
padding: 12px;
|
||||||
|
color: #444;
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.tabs .tab-list .tab.active a {
|
||||||
|
border-bottom: 3px solid #f5e6ab;
|
||||||
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
button {
|
button {
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
|
|
@ -106,7 +130,8 @@
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls {
|
section.posts header.controls {
|
||||||
|
margin-top: -16px;
|
||||||
padding: 0 0 36px;
|
padding: 0 0 36px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template:
|
grid-template:
|
||||||
|
|
@ -115,11 +140,11 @@
|
||||||
"c";
|
"c";
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .search-form input {
|
section.posts header.controls .search-form input {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .loading-indicator {
|
section.posts header.controls .loading-indicator {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
@ -127,7 +152,7 @@
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .loading-indicator .arc {
|
section.posts header.controls .loading-indicator .arc {
|
||||||
/* svg */
|
/* svg */
|
||||||
fill: transparent;
|
fill: transparent;
|
||||||
stroke-width: 20;
|
stroke-width: 20;
|
||||||
|
|
@ -144,49 +169,50 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .buttons {
|
section.posts header.controls .buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .post-info {
|
section.posts header.controls .post-info {
|
||||||
grid-area: b;
|
grid-area: b;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .search-form {
|
section.posts header.controls .search-form {
|
||||||
grid-area: c;
|
grid-area: c;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .buttons {
|
section.posts header.controls .buttons {
|
||||||
grid-area: a;
|
grid-area: a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 640px) {
|
@media screen and (min-width: 640px) {
|
||||||
section.posts .controls {
|
section.posts header.controls {
|
||||||
grid-template:
|
grid-template:
|
||||||
"a a"
|
"a a"
|
||||||
"b c";
|
"b c";
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .post-info {
|
section.posts header.controls .post-info {
|
||||||
grid-area: a;
|
grid-area: a;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .search-form {
|
section.posts header.controls .search-form {
|
||||||
grid-area: b;
|
grid-area: b;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .search-form input {
|
section.posts header.controls .search-form input {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .buttons {
|
section.posts header.controls .buttons {
|
||||||
grid-area: c;
|
grid-area: c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section.posts .controls .control-button {
|
section.posts .controls .control-button {
|
||||||
|
/* used for both header and post controls */
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
margin: 6px 0 6px 12px;
|
margin: 6px 0 6px 12px;
|
||||||
background: #f5e6ab;
|
background: #f5e6ab;
|
||||||
|
|
@ -261,6 +287,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
section.help ul {
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
padding: .16rem .24rem;
|
padding: .16rem .24rem;
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@
|
||||||
(defn search [{:keys [query]}]
|
(defn search [{:keys [query]}]
|
||||||
[:input {:placeholder "Start typing to search…"
|
[:input {:placeholder "Start typing to search…"
|
||||||
:type "search"
|
:type "search"
|
||||||
:initial-value query
|
:default-value query
|
||||||
:on-change (fn [e]
|
:on-change (fn [e]
|
||||||
(let [query (.. e -target -value)]
|
(let [query (.. e -target -value)]
|
||||||
(swap! state assoc-in [:section/posts :query] (if (str/blank? query) nil query))))}])
|
(swap! state assoc-in [:section/posts :query] (if (str/blank? query) nil query))))}])
|
||||||
|
|
@ -534,17 +534,56 @@
|
||||||
(defn help-section []
|
(defn help-section []
|
||||||
[:section.help
|
[:section.help
|
||||||
[:h2 "Help"]
|
[:h2 "Help"]
|
||||||
[:h3 "Search Syntax"]
|
[:h3 "Search"]
|
||||||
[:ul.search-syntax
|
[:ul
|
||||||
[:li "Three are several places that we take into account when looking for query results"
|
[:li "Type into the input field at the top of the list of posts. Results are updated in real time."]
|
||||||
|
[:li "Search is case-insensitive, differences in upper- and lower-case are ignored"]
|
||||||
|
[:li "The search is split into terms whenever there is a space. Each term needs to match, but order does not matter."
|
||||||
|
[:ul
|
||||||
|
[:li "For example " [:code "hello world"] " will search for both" [:code "hello"] " and "
|
||||||
|
[:code "world"] ", but will match " [:code "hello world!"] " as well as " [:code "world, did you say hello today?"]]
|
||||||
|
[:li "To search for a longer phrase that contains spaces, " [:code "\"quote it like this\""]]]]
|
||||||
|
[:li "By default terms are fuzzy and can match in any of the following places:"
|
||||||
[:ul
|
[:ul
|
||||||
[:li "Post content"]
|
[:li "Post content"]
|
||||||
|
[:li "Description of any attachment"]
|
||||||
[:li "Creator of the post"]
|
[:li "Creator of the post"]
|
||||||
[:li "Any mentioned account"]]]
|
[:li "Any mentioned account"]]]
|
||||||
[:li "Upper- and lowercase does not make a difference."]
|
[:li "By default Lodestone tries to turn terms into regular expressions."
|
||||||
[:li "All words are matched separately. They do not have to appear in the post in the same order, but they all have to appear."]
|
[:ul
|
||||||
[:li "If you want to search verbatim for a phrase, \"quote it like this\""]
|
[:li [:code "fossi.*ergy"] " will match \"fossile energy\"."]
|
||||||
[:li "Lodestone tries to turn words into regular expressions. " [:code "fossi.*ergy"] " will match \"fossile energy\"." [:code "bo?ar"] " will match both boar and bar."]]])
|
[:li [:code "bo?ar"] " will match both boar and bar."]
|
||||||
|
[:li [:code "\"for (this|that)\""] " will match \"for this\" and \"for that\"."]]]
|
||||||
|
[:li "Terms can be prefixed to change default behavior:"
|
||||||
|
[:ul
|
||||||
|
[:li "Date prefixes can be used to restrict a date range for your results. Dates are meant to be given like "
|
||||||
|
[:code "2025-11-22"] " / " [:code "YYYY-MM-DD"] ", but month and day are optional."
|
||||||
|
[:ul
|
||||||
|
[:li [:code "date:2023"] " will match posts created after 2023-01-01 and before 2023-12-31."]
|
||||||
|
[:li [:code "before:2024-10-31"] " will match posts created before 2024-10-31."]
|
||||||
|
[:li [:code "after:2025-02"] " will match any post created on or after 2025-02-01."]]]
|
||||||
|
[:li "User prefixes restrict results based on who posted or who was addressed. "
|
||||||
|
[:ul
|
||||||
|
[:li [:code "from:xy"] " matches all posts by all users whose name starts with " [:code "xy"]]
|
||||||
|
[:li [:code "to:xy"] " matches all posts where any mentioned user has a name that starts with " [:code "xy"]]]]]]]])
|
||||||
|
|
||||||
|
;; tab bar
|
||||||
|
|
||||||
|
(defn- switch-section! [section]
|
||||||
|
(fn [e]
|
||||||
|
(.preventDefault e)
|
||||||
|
(swap! state assoc :section section)))
|
||||||
|
|
||||||
|
(defn tabs [{:keys [section]}]
|
||||||
|
(when-not (= section :login)
|
||||||
|
[:nav.tabs
|
||||||
|
(into [:ul.tab-list]
|
||||||
|
(map
|
||||||
|
(fn [[sec label]]
|
||||||
|
[:li.tab
|
||||||
|
{:class [(when (= section sec) "active")]}
|
||||||
|
[:a {:href "#" :on-click (switch-section! sec)} label]]))
|
||||||
|
[[:posts "Posts"] [:help "Help"]])]))
|
||||||
|
|
||||||
;; the component tying it all together
|
;; the component tying it all together
|
||||||
|
|
||||||
|
|
@ -552,6 +591,7 @@
|
||||||
(let [section (:section @state)
|
(let [section (:section @state)
|
||||||
posts (:section/posts @state)]
|
posts (:section/posts @state)]
|
||||||
[:main#app
|
[:main#app
|
||||||
|
[tabs {:section section}]
|
||||||
(case section
|
(case section
|
||||||
:login [login-section]
|
:login [login-section]
|
||||||
:posts [posts-section {:posts posts}]
|
:posts [posts-section {:posts posts}]
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,12 @@
|
||||||
(defn date-after-matcher [term]
|
(defn date-after-matcher [term]
|
||||||
(filter #(> (j/get % :created_at) term)))
|
(filter #(> (j/get % :created_at) term)))
|
||||||
|
|
||||||
|
(defn date-on-matcher [term]
|
||||||
|
(let [date-parts (str/split term #"-")
|
||||||
|
x (parse-long (last date-parts))]
|
||||||
|
(comp (date-after-matcher term)
|
||||||
|
(date-before-matcher (str/join "-" (conj (vec (butlast date-parts)) (inc x)))))))
|
||||||
|
|
||||||
(defn token->term [token]
|
(defn token->term [token]
|
||||||
(str/replace token #"^\w+:" ""))
|
(str/replace token #"^\w+:" ""))
|
||||||
|
|
||||||
|
|
@ -58,6 +64,7 @@
|
||||||
(str/starts-with? token "to:") (mention-matcher (token->term token))
|
(str/starts-with? token "to:") (mention-matcher (token->term token))
|
||||||
(str/starts-with? token "before:") (date-before-matcher (token->term token))
|
(str/starts-with? token "before:") (date-before-matcher (token->term token))
|
||||||
(str/starts-with? token "after:") (date-after-matcher (token->term token))
|
(str/starts-with? token "after:") (date-after-matcher (token->term token))
|
||||||
|
(str/starts-with? token "date:")(date-on-matcher (token->term token))
|
||||||
:else (text-matcher token))))
|
:else (text-matcher token))))
|
||||||
(reduce comp))
|
(reduce comp))
|
||||||
(filter (constantly true))))
|
(filter (constantly true))))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue