add: user links and location
This commit is contained in:
parent
5fafc8d7b9
commit
140a11ff72
18 changed files with 442 additions and 222 deletions
91
Cargo.lock
generated
91
Cargo.lock
generated
|
@ -1603,6 +1603,7 @@ checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1667,6 +1668,15 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
|
@ -2379,6 +2389,27 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.95"
|
||||||
|
@ -2587,7 +2618,7 @@ dependencies = [
|
||||||
"built",
|
"built",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"interpolate_name",
|
"interpolate_name",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
"log",
|
"log",
|
||||||
|
@ -3028,6 +3059,51 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_valid"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b615bed66931a7a9809b273937adc8a402d038b1e509d027fcaf62f084d33d1"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"paste",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_valid_derive",
|
||||||
|
"serde_valid_literal",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_valid_derive"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fa1a5a21ea5aab06d2e6a6b59837d450fb2be9695be97735a711edfbe79ea07"
|
||||||
|
dependencies = [
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"paste",
|
||||||
|
"proc-macro-error2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.104",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_valid_literal"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd07331596ea967dccf9a35bde71ecd757490e09827b938a5c6226c648e3a25e"
|
||||||
|
dependencies = [
|
||||||
|
"paste",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
@ -3214,6 +3290,12 @@ dependencies = [
|
||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
|
@ -3423,6 +3505,7 @@ dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_valid",
|
||||||
"tetratto-l10n 12.0.0",
|
"tetratto-l10n 12.0.0",
|
||||||
"tetratto-shared 12.0.6",
|
"tetratto-shared 12.0.6",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -4027,6 +4110,12 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
@ -175,6 +175,7 @@ version = "1.0.0"
|
||||||
"settings:tab.general" = "General"
|
"settings:tab.general" = "General"
|
||||||
"settings:tab.account" = "Account"
|
"settings:tab.account" = "Account"
|
||||||
"settings:tab.profile" = "Profile"
|
"settings:tab.profile" = "Profile"
|
||||||
|
"settings:tab.experience" = "Experience"
|
||||||
"settings:tab.theme" = "Theme"
|
"settings:tab.theme" = "Theme"
|
||||||
"settings:tab.sessions" = "Sessions"
|
"settings:tab.sessions" = "Sessions"
|
||||||
"settings:tab.grants" = "Grants"
|
"settings:tab.grants" = "Grants"
|
||||||
|
|
|
@ -199,6 +199,34 @@ body:not(.use_system_font) {
|
||||||
& input,
|
& input,
|
||||||
& textarea {
|
& textarea {
|
||||||
font-variation-settings: "wght" 325;
|
font-variation-settings: "wght" 325;
|
||||||
|
|
||||||
|
& h1 {
|
||||||
|
font-variation-settings: "wght" 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h2 {
|
||||||
|
font-variation-settings: "wght" 550;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h3 {
|
||||||
|
font-variation-settings: "wght" 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h4 {
|
||||||
|
font-variation-settings: "wght" 450;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h5 {
|
||||||
|
font-variation-settings: "wght" 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h6 {
|
||||||
|
font-variation-settings: "wght" 350;
|
||||||
|
}
|
||||||
|
|
||||||
|
& b {
|
||||||
|
font-variation-settings: "wght" 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1256,6 +1256,11 @@ details summary::-webkit-details-marker {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details summary.button {
|
||||||
|
height: max-content;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
details[open] > summary {
|
details[open] > summary {
|
||||||
position: relative;
|
position: relative;
|
||||||
color: var(--color-text-lowered) !important;
|
color: var(--color-text-lowered) !important;
|
||||||
|
@ -1288,7 +1293,7 @@ details.accordion {
|
||||||
details.accordion summary {
|
details.accordion summary {
|
||||||
background: var(--color-lowered);
|
background: var(--color-lowered);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
padding: var(--pad-3) var(--pad-4);
|
padding: var(--pad-3) var(--pad-4) !important;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))))
|
(str (text "general:action.save")))))))
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
("ui_ident" "danger_zone")
|
("ui_ident" "danger_zone")
|
||||||
|
@ -170,7 +170,7 @@
|
||||||
("onclick" "save_context()")
|
("onclick" "save_context()")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))
|
(str (text "general:action.save"))))
|
||||||
(a
|
(a
|
||||||
("href" "/community/{{ community.title }}")
|
("href" "/community/{{ community.title }}")
|
||||||
("class" "button secondary")
|
("class" "button secondary")
|
||||||
|
@ -273,7 +273,7 @@
|
||||||
("onclick" "update_user_role(document.getElementById('uid').value, document.getElementById('role').value)")
|
("onclick" "update_user_role(document.getElementById('uid').value, document.getElementById('role').value)")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))
|
(str (text "general:action.save")))))
|
||||||
(div
|
(div
|
||||||
("class" "card flex flex_col gap_2")
|
("class" "card flex flex_col gap_2")
|
||||||
("id" "permission_builder"))))
|
("id" "permission_builder"))))
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))))
|
(str (text "general:action.save"))))))
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
(div
|
(div
|
||||||
|
@ -120,7 +120,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))))
|
(str (text "general:action.save"))))))
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
(div
|
(div
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))))
|
(str (text "general:action.save"))))))
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
(div
|
(div
|
||||||
|
@ -185,7 +185,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))))
|
(str (text "general:action.save"))))))
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
(div
|
(div
|
||||||
|
|
|
@ -255,7 +255,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))))))
|
(str (text "general:action.save"))))))))
|
||||||
|
|
||||||
; users should also be able to manage the journal's sub directories here
|
; users should also be able to manage the journal's sub directories here
|
||||||
(details
|
(details
|
||||||
|
|
|
@ -354,7 +354,7 @@
|
||||||
(str (text "forge:tab.tickets"))))
|
(str (text "forge:tab.tickets"))))
|
||||||
(text "{%- endmacro %}")
|
(text "{%- endmacro %}")
|
||||||
|
|
||||||
(text "{% macro profile_settings_nav_options() -%}")
|
(text "{% macro user_settings_nav_options() -%}")
|
||||||
(a
|
(a
|
||||||
("data-tab-button" "account")
|
("data-tab-button" "account")
|
||||||
("class" "active")
|
("class" "active")
|
||||||
|
@ -368,6 +368,12 @@
|
||||||
(text "{{ icon \"user-round\" }}")
|
(text "{{ icon \"user-round\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"settings:tab.profile\" }}")))
|
(text "{{ text \"settings:tab.profile\" }}")))
|
||||||
|
(a
|
||||||
|
("data-tab-button" "experience")
|
||||||
|
("href" "#/experience")
|
||||||
|
(text "{{ icon \"settings-2\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"settings:tab.experience\" }}")))
|
||||||
(a
|
(a
|
||||||
("data-tab-button" "theme")
|
("data-tab-button" "theme")
|
||||||
("href" "#/theme")
|
("href" "#/theme")
|
||||||
|
@ -393,4 +399,10 @@
|
||||||
(text "{{ icon \"book-user\" }}")
|
(text "{{ icon \"book-user\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"settings:tab.close_friends\" }}")))
|
(text "{{ text \"settings:tab.close_friends\" }}")))
|
||||||
|
(a
|
||||||
|
("data-tab-button" "presets")
|
||||||
|
("href" "#/presets")
|
||||||
|
(icon (text "cooking-pot"))
|
||||||
|
(span
|
||||||
|
(str (text "settings:tab.presets"))))
|
||||||
(text "{%- endmacro %}")
|
(text "{%- endmacro %}")
|
||||||
|
|
|
@ -358,7 +358,7 @@
|
||||||
("onclick" "update_user_role(Number.parseInt(document.getElementById('role').value))")
|
("onclick" "update_user_role(Number.parseInt(document.getElementById('role').value))")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))
|
(str (text "general:action.save")))))
|
||||||
(div
|
(div
|
||||||
("class" "card lowered flex flex_col gap_2")
|
("class" "card lowered flex flex_col gap_2")
|
||||||
("id" "permission_builder")))
|
("id" "permission_builder")))
|
||||||
|
@ -376,7 +376,7 @@
|
||||||
("onclick" "update_user_secondary_role(Number.parseInt(document.getElementById('secondary_role').value))")
|
("onclick" "update_user_secondary_role(Number.parseInt(document.getElementById('secondary_role').value))")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))
|
(str (text "general:action.save")))))
|
||||||
(div
|
(div
|
||||||
("class" "card lowered flex flex_col gap_2")
|
("class" "card lowered flex flex_col gap_2")
|
||||||
("id" "secondary_permission_builder")))
|
("id" "secondary_permission_builder")))
|
||||||
|
|
|
@ -148,7 +148,7 @@
|
||||||
("onclick" "save_context()")
|
("onclick" "save_context()")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))
|
(str (text "general:action.save"))))
|
||||||
(script
|
(script
|
||||||
(text "setTimeout(async () => {
|
(text "setTimeout(async () => {
|
||||||
const ui = await ns(\"ui\");
|
const ui = await ns(\"ui\");
|
||||||
|
@ -286,7 +286,7 @@
|
||||||
("class" "flex gap_2")
|
("class" "flex gap_2")
|
||||||
(text "{{ components::emoji_picker(element_id=\"new_content\", render_dialog=false) }}")
|
(text "{{ components::emoji_picker(element_id=\"new_content\", render_dialog=false) }}")
|
||||||
(button
|
(button
|
||||||
(text "{{ text \"general:action.save\" }}")))))
|
(str (text "general:action.save"))))))
|
||||||
(script
|
(script
|
||||||
(text "async function edit_post_from_form(e) {
|
(text "async function edit_post_from_form(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
("class" "fade")
|
("class" "fade")
|
||||||
(text "{{ profile.username }}"))))
|
(text "{{ profile.username }}"))))
|
||||||
(div
|
(div
|
||||||
("class" "card flex flex_col items_center gap_2")
|
("class" "card flex flex_col items_center small gap_2")
|
||||||
("id" "social")
|
("id" "social")
|
||||||
(text "{% if profile.settings.status -%}")
|
(text "{% if profile.settings.status -%}")
|
||||||
(p
|
(p
|
||||||
|
@ -159,7 +159,20 @@
|
||||||
(div
|
(div
|
||||||
("id" "bio")
|
("id" "bio")
|
||||||
("class" "card small no_p_margin")
|
("class" "card small no_p_margin")
|
||||||
(text "{{ profile.settings.biography|markdown|safe }}"))
|
(text "{{ profile.settings.biography|markdown|safe }}")
|
||||||
|
|
||||||
|
(text "{% if profile.settings.location|length > 0 -%}")
|
||||||
|
(span ("class" "flex items_center gap_2") (icon (text "map-pin")) (text "{{ profile.settings.location }}"))
|
||||||
|
(text "{%- endif %}")
|
||||||
|
|
||||||
|
(text "{% for link in profile.settings.links -%}")
|
||||||
|
(span
|
||||||
|
("class" "flex items_center gap_2")
|
||||||
|
(icon (text "link"))
|
||||||
|
(a
|
||||||
|
("href" "{{ link[1] }}")
|
||||||
|
(text "{{ link[0] }}")))
|
||||||
|
(text "{%- endfor %}"))
|
||||||
(div
|
(div
|
||||||
("class" "card flex flex_col gap_2")
|
("class" "card flex flex_col gap_2")
|
||||||
(text "{% if user -%}")
|
(text "{% if user -%}")
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
; nav desktop
|
; nav desktop
|
||||||
(menu
|
(menu
|
||||||
("class" "desktop col")
|
("class" "desktop col")
|
||||||
(text "{{ macros::profile_settings_nav_options() }}"))
|
(text "{{ macros::user_settings_nav_options() }}"))
|
||||||
|
|
||||||
; content
|
; content
|
||||||
(main
|
(main
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
(span ("class" "current_tab_text") (text "account")))
|
(span ("class" "current_tab_text") (text "account")))
|
||||||
(div
|
(div
|
||||||
("class" "inner left")
|
("class" "inner left")
|
||||||
(text "{{ macros::profile_settings_nav_options() }}"))))
|
(text "{{ macros::user_settings_nav_options() }}"))))
|
||||||
|
|
||||||
; ...
|
; ...
|
||||||
(div
|
(div
|
||||||
|
@ -43,25 +43,11 @@
|
||||||
("data-tab" "presets")
|
("data-tab" "presets")
|
||||||
(div
|
(div
|
||||||
("class" "card lowered flex flex_col gap_2")
|
("class" "card lowered flex flex_col gap_2")
|
||||||
(a
|
|
||||||
("href" "#/account")
|
|
||||||
("class" "button secondary")
|
|
||||||
(icon (text "arrow-left"))
|
|
||||||
(span
|
|
||||||
(str (text "general:action.back"))))
|
|
||||||
(div
|
|
||||||
("class" "card_nest")
|
|
||||||
(div
|
|
||||||
("class" "card flex items_center gap_2 small")
|
|
||||||
(icon (text "cooking-pot"))
|
|
||||||
(span
|
|
||||||
(str (text "settings:tab.presets"))))
|
|
||||||
(div
|
|
||||||
("class" "card flex flex_col gap_2 secondary")
|
|
||||||
(p (text "Not sure where to start? Try some settings presets!"))
|
(p (text "Not sure where to start? Try some settings presets!"))
|
||||||
(details
|
(details
|
||||||
("class" "w_full accordion")
|
("class" "w_full accordion")
|
||||||
(summary
|
(summary
|
||||||
|
("class" "button raised")
|
||||||
(icon (text "rss"))
|
(icon (text "rss"))
|
||||||
(text "Microblogging"))
|
(text "Microblogging"))
|
||||||
|
|
||||||
|
@ -77,6 +63,7 @@
|
||||||
(details
|
(details
|
||||||
("class" "w_full accordion")
|
("class" "w_full accordion")
|
||||||
(summary
|
(summary
|
||||||
|
("class" "button raised")
|
||||||
(icon (text "message-circle-heart"))
|
(icon (text "message-circle-heart"))
|
||||||
(text "Q&A"))
|
(text "Q&A"))
|
||||||
|
|
||||||
|
@ -92,6 +79,7 @@
|
||||||
(details
|
(details
|
||||||
("class" "w_full accordion")
|
("class" "w_full accordion")
|
||||||
(summary
|
(summary
|
||||||
|
("class" "button raised")
|
||||||
(icon (text "key"))
|
(icon (text "key"))
|
||||||
(text "Private"))
|
(text "Private"))
|
||||||
|
|
||||||
|
@ -107,6 +95,7 @@
|
||||||
(details
|
(details
|
||||||
("class" "w_full accordion")
|
("class" "w_full accordion")
|
||||||
(summary
|
(summary
|
||||||
|
("class" "button raised")
|
||||||
(icon (text "eye-closed"))
|
(icon (text "eye-closed"))
|
||||||
(text "NSFW"))
|
(text "NSFW"))
|
||||||
|
|
||||||
|
@ -117,7 +106,7 @@
|
||||||
(button
|
(button
|
||||||
("onclick" "apply_preset(PRESET_NSFW)")
|
("onclick" "apply_preset(PRESET_NSFW)")
|
||||||
(icon (text "settings"))
|
(icon (text "settings"))
|
||||||
(str (text "general:action.apply")))))))))
|
(str (text "general:action.apply")))))))
|
||||||
|
|
||||||
(div
|
(div
|
||||||
("class" "w_full flex flex_col gap_2")
|
("class" "w_full flex flex_col gap_2")
|
||||||
|
@ -180,61 +169,6 @@
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"settings:tab.billing\" }}"))))
|
(text "{{ text \"settings:tab.billing\" }}"))))
|
||||||
(text "{%- endif %}")
|
(text "{%- endif %}")
|
||||||
|
|
||||||
(div
|
|
||||||
("class" "card_nest")
|
|
||||||
("ui_ident" "home_timeline")
|
|
||||||
(div
|
|
||||||
("class" "card small")
|
|
||||||
(b
|
|
||||||
(text "Home timeline")))
|
|
||||||
(div
|
|
||||||
("class" "card")
|
|
||||||
(select
|
|
||||||
("onchange" "window.SETTING_SET_FUNCTIONS[0]('default_timeline', event.target.selectedOptions[0].value.startsWith('{') ? JSON.parse(event.target.selectedOptions[0].value) : event.target.selectedOptions[0].value)")
|
|
||||||
(option
|
|
||||||
("value" "MyCommunities")
|
|
||||||
("selected" "{% if home == '/' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "My communities"))
|
|
||||||
(option
|
|
||||||
("value" "MyCommunitiesQuestions")
|
|
||||||
("selected" "{% if home == '/questions' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "My communities (questions)"))
|
|
||||||
(option
|
|
||||||
("value" "PopularPosts")
|
|
||||||
("selected" "{% if home == '/popular' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "Popular"))
|
|
||||||
(option
|
|
||||||
("value" "PopularQuestions")
|
|
||||||
("selected" "{% if home == '/popular/questions' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "Popular (questions)"))
|
|
||||||
(option
|
|
||||||
("value" "FollowingPosts")
|
|
||||||
("selected" "{% if home == '/following' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "Following"))
|
|
||||||
(option
|
|
||||||
("value" "FollowingQuestions")
|
|
||||||
("selected" "{% if home == '/following/questions' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "Following (questions)"))
|
|
||||||
(option
|
|
||||||
("value" "AllPosts")
|
|
||||||
("selected" "{% if home == '/all' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "All"))
|
|
||||||
(option
|
|
||||||
("value" "AllQuestions")
|
|
||||||
("selected" "{% if home == '/all/questions' -%}true{% else %}false{%- endif %}")
|
|
||||||
(text "All (questions)"))
|
|
||||||
(text "{% for stack in stacks %}")
|
|
||||||
(text "<option
|
|
||||||
value='{\"Stack\":\"{{ stack.id }}\"}'
|
|
||||||
selected=\"{% if home is ending_with(stack.id|as_str) -%}true{% else %}false{%- endif %}\"
|
|
||||||
>
|
|
||||||
{{ stack.name }} (stack)
|
|
||||||
</option>")
|
|
||||||
(text "{% endfor %}"))
|
|
||||||
(span
|
|
||||||
("class" "fade")
|
|
||||||
(text "This represents the timeline the home button takes you to."))))
|
|
||||||
(div
|
(div
|
||||||
("class" "card_nest desktop")
|
("class" "card_nest desktop")
|
||||||
("ui_ident" "notifications")
|
("ui_ident" "notifications")
|
||||||
|
@ -282,7 +216,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))))
|
(str (text "general:action.save")))))))
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
("ui_ident" "delete_account")
|
("ui_ident" "delete_account")
|
||||||
|
@ -337,7 +271,7 @@
|
||||||
("id" "save_button")
|
("id" "save_button")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))
|
(str (text "general:action.save")))))
|
||||||
(div
|
(div
|
||||||
("class" "w_full flex flex_col gap_2 hidden")
|
("class" "w_full flex flex_col gap_2 hidden")
|
||||||
("data-tab" "account/security")
|
("data-tab" "account/security")
|
||||||
|
@ -444,7 +378,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}")))))))))
|
(str (text "general:action.save"))))))))))
|
||||||
(div
|
(div
|
||||||
("class" "w_full flex flex_col gap_2 hidden")
|
("class" "w_full flex flex_col gap_2 hidden")
|
||||||
("data-tab" "account/following")
|
("data-tab" "account/following")
|
||||||
|
@ -991,13 +925,13 @@
|
||||||
(span
|
(span
|
||||||
("class" "fade")
|
("class" "fade")
|
||||||
(text "Use an image of 1100x350px for the best results."))))
|
(text "Use an image of 1100x350px for the best results."))))
|
||||||
|
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
("ui_ident" "default_profile_page")
|
("ui_ident" "default_profile_page")
|
||||||
(div
|
(div
|
||||||
("class" "card small")
|
("class" "card small")
|
||||||
(b
|
(b (text "Default profile tab")))
|
||||||
(text "Default profile tab")))
|
|
||||||
(div
|
(div
|
||||||
("class" "card")
|
("class" "card")
|
||||||
(select
|
(select
|
||||||
|
@ -1013,10 +947,36 @@
|
||||||
(span
|
(span
|
||||||
("class" "fade")
|
("class" "fade")
|
||||||
(text "This represents the timeline that is shown on your profile by default."))))
|
(text "This represents the timeline that is shown on your profile by default."))))
|
||||||
|
|
||||||
|
(div
|
||||||
|
("class" "card_nest")
|
||||||
|
("ui_ident" "user_links")
|
||||||
|
(div
|
||||||
|
("class" "card small")
|
||||||
|
(b (text "My links")))
|
||||||
|
(div
|
||||||
|
("class" "card flex flex_col gap_2")
|
||||||
|
(button
|
||||||
|
("onclick" "add_link()")
|
||||||
|
(icon (text "plus"))
|
||||||
|
(text "Add link"))
|
||||||
|
|
||||||
|
(ul ("id" "user_links")))))
|
||||||
|
(button
|
||||||
|
("onclick" "save_settings()")
|
||||||
|
("id" "save_button")
|
||||||
|
(icon (text "check"))
|
||||||
|
(span
|
||||||
|
(str (text "general:action.save")))))
|
||||||
|
(div
|
||||||
|
("class" "w_full hidden flex flex_col gap_2")
|
||||||
|
("data-tab" "experience")
|
||||||
|
(div
|
||||||
|
("class" "card lowered flex flex_col gap_2")
|
||||||
|
("id" "experience_settings")
|
||||||
(div
|
(div
|
||||||
("class" "flex flex_col gap_2")
|
("class" "flex flex_col gap_2")
|
||||||
("ui_ident" "show_presets")
|
("ui_ident" "show_presets")
|
||||||
(hr ("class" "margin"))
|
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
(div
|
(div
|
||||||
|
@ -1028,13 +988,67 @@
|
||||||
(p
|
(p
|
||||||
(text "Quickly set up your account with ")
|
(text "Quickly set up your account with ")
|
||||||
(a ("href" "/settings#/presets") (text "settings presets"))
|
(a ("href" "/settings#/presets") (text "settings presets"))
|
||||||
(text "!"))))))
|
(text "!")))))
|
||||||
|
(div
|
||||||
|
("class" "card_nest")
|
||||||
|
("ui_ident" "home_timeline")
|
||||||
|
(div
|
||||||
|
("class" "card small")
|
||||||
|
(b
|
||||||
|
(text "Home timeline")))
|
||||||
|
(div
|
||||||
|
("class" "card")
|
||||||
|
(select
|
||||||
|
("onchange" "window.SETTING_SET_FUNCTIONS[0]('default_timeline', event.target.selectedOptions[0].value.startsWith('{') ? JSON.parse(event.target.selectedOptions[0].value) : event.target.selectedOptions[0].value)")
|
||||||
|
(option
|
||||||
|
("value" "MyCommunities")
|
||||||
|
("selected" "{% if home == '/' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "My communities"))
|
||||||
|
(option
|
||||||
|
("value" "MyCommunitiesQuestions")
|
||||||
|
("selected" "{% if home == '/questions' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "My communities (questions)"))
|
||||||
|
(option
|
||||||
|
("value" "PopularPosts")
|
||||||
|
("selected" "{% if home == '/popular' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "Popular"))
|
||||||
|
(option
|
||||||
|
("value" "PopularQuestions")
|
||||||
|
("selected" "{% if home == '/popular/questions' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "Popular (questions)"))
|
||||||
|
(option
|
||||||
|
("value" "FollowingPosts")
|
||||||
|
("selected" "{% if home == '/following' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "Following"))
|
||||||
|
(option
|
||||||
|
("value" "FollowingQuestions")
|
||||||
|
("selected" "{% if home == '/following/questions' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "Following (questions)"))
|
||||||
|
(option
|
||||||
|
("value" "AllPosts")
|
||||||
|
("selected" "{% if home == '/all' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "All"))
|
||||||
|
(option
|
||||||
|
("value" "AllQuestions")
|
||||||
|
("selected" "{% if home == '/all/questions' -%}true{% else %}false{%- endif %}")
|
||||||
|
(text "All (questions)"))
|
||||||
|
(text "{% for stack in stacks %}")
|
||||||
|
(text "<option
|
||||||
|
value='{\"Stack\":\"{{ stack.id }}\"}'
|
||||||
|
selected=\"{% if home is ending_with(stack.id|as_str) -%}true{% else %}false{%- endif %}\"
|
||||||
|
>
|
||||||
|
{{ stack.name }} (stack)
|
||||||
|
</option>")
|
||||||
|
(text "{% endfor %}"))
|
||||||
|
(span
|
||||||
|
("class" "fade")
|
||||||
|
(text "This represents the timeline the home button takes you to.")))))
|
||||||
(button
|
(button
|
||||||
("onclick" "save_settings()")
|
("onclick" "save_settings()")
|
||||||
("id" "save_button")
|
("id" "save_button")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))
|
(str (text "general:action.save")))))
|
||||||
(div
|
(div
|
||||||
("class" "card w_full lowered hidden flex flex_col gap_2")
|
("class" "card w_full lowered hidden flex flex_col gap_2")
|
||||||
("data-tab" "sessions")
|
("data-tab" "sessions")
|
||||||
|
@ -1193,7 +1207,7 @@
|
||||||
("id" "save_button")
|
("id" "save_button")
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))
|
(str (text "general:action.save")))))
|
||||||
(div
|
(div
|
||||||
("class" "card w_full lowered hidden flex flex_col gap_2")
|
("class" "card w_full lowered hidden flex flex_col gap_2")
|
||||||
("data-tab" "grants")
|
("data-tab" "grants")
|
||||||
|
@ -1595,7 +1609,7 @@
|
||||||
`data:image/png;base64,${qr}`;
|
`data:image/png;base64,${qr}`;
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
\"totp_recovery_codes\",
|
\"totp_recovery_codes\",
|
||||||
).innerText = recovery_codes.join(\"\n\");
|
).innerText = recovery_codes.join(\"\\n\");
|
||||||
|
|
||||||
document.getElementById(\"totp_stuff\").style.display =
|
document.getElementById(\"totp_stuff\").style.display =
|
||||||
\"contents\";
|
\"contents\";
|
||||||
|
@ -1787,12 +1801,13 @@
|
||||||
document.getElementById(\"account_settings\");
|
document.getElementById(\"account_settings\");
|
||||||
const profile_settings =
|
const profile_settings =
|
||||||
document.getElementById(\"profile_settings\");
|
document.getElementById(\"profile_settings\");
|
||||||
|
const experience_settings =
|
||||||
|
document.getElementById(\"experience_settings\");
|
||||||
const theme_settings = document.getElementById(\"theme_settings\");
|
const theme_settings = document.getElementById(\"theme_settings\");
|
||||||
|
|
||||||
ui.refresh_container(account_settings, [
|
ui.refresh_container(account_settings, [
|
||||||
\"supporter_ad\",
|
\"supporter_ad\",
|
||||||
\"account_settings_tabs\",
|
\"account_settings_tabs\",
|
||||||
\"home_timeline\",
|
|
||||||
\"notifications\",
|
\"notifications\",
|
||||||
\"change_username\",
|
\"change_username\",
|
||||||
\"delete_account\",
|
\"delete_account\",
|
||||||
|
@ -1802,6 +1817,11 @@
|
||||||
\"change_avatar\",
|
\"change_avatar\",
|
||||||
\"change_banner\",
|
\"change_banner\",
|
||||||
\"default_profile_page\",
|
\"default_profile_page\",
|
||||||
|
\"user_links\",
|
||||||
|
]);
|
||||||
|
ui.refresh_container(experience_settings, [
|
||||||
|
\"supporter_ad\",
|
||||||
|
\"home_timeline\",
|
||||||
\"show_presets\",
|
\"show_presets\",
|
||||||
]);
|
]);
|
||||||
ui.refresh_container(theme_settings, [
|
ui.refresh_container(theme_settings, [
|
||||||
|
@ -1814,7 +1834,7 @@
|
||||||
]);
|
]);
|
||||||
|
|
||||||
ui.generate_settings_ui(
|
ui.generate_settings_ui(
|
||||||
account_settings,
|
profile_settings,
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
[\"display_name\", \"Display name\"],
|
[\"display_name\", \"Display name\"],
|
||||||
|
@ -1835,50 +1855,23 @@
|
||||||
'<span class=\"fade\">This biography is only shown to users you are not following while your account is private.</span>',
|
'<span class=\"fade\">This biography is only shown to users you are not following while your account is private.</span>',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
[\"location\", \"Location\"],
|
||||||
|
\"{{ profile.settings.location }}\",
|
||||||
|
\"input\",
|
||||||
|
],
|
||||||
[[\"status\", \"Status\"], settings.status, \"textarea\"],
|
[[\"status\", \"Status\"], settings.status, \"textarea\"],
|
||||||
[
|
[
|
||||||
[\"warning\", \"Profile warning\"],
|
[\"warning\", \"Profile warning\"],
|
||||||
settings.warning,
|
settings.warning,
|
||||||
\"textarea\",
|
\"textarea\",
|
||||||
],
|
],
|
||||||
[[\"muted\", \"Muted phrases\"], settings.muted.join(\"\\n\"), \"textarea\", {
|
|
||||||
embed_html:
|
|
||||||
'<span class=\"fade\">Muted phrases should all be on new lines.</span>',
|
|
||||||
}],
|
|
||||||
[[], \"Accessibility\", \"title\"],
|
|
||||||
[
|
|
||||||
[\"large_text\", \"Increase UI text size\"],
|
|
||||||
\"{{ profile.settings.large_text }}\",
|
|
||||||
\"checkbox\",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[\"use_system_font\", \"Always use system font instead\"],
|
|
||||||
\"{{ profile.settings.use_system_font }}\",
|
|
||||||
\"checkbox\",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[\"paged_timelines\", \"Make timelines paged instead of infinitely scrolled\"],
|
|
||||||
\"{{ profile.settings.paged_timelines }}\",
|
|
||||||
\"checkbox\",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[\"auto_clear_notifs\", \"Automatically clear all notifications when you open the notifications page\"],
|
|
||||||
\"{{ profile.settings.auto_clear_notifs }}\",
|
|
||||||
\"checkbox\",
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
settings,
|
settings,
|
||||||
{
|
|
||||||
muted: (new_muted) => {
|
|
||||||
settings.muted = new_muted
|
|
||||||
.split(\"\\n\")
|
|
||||||
.map((t) => t.trim());
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ui.generate_settings_ui(
|
ui.generate_settings_ui(
|
||||||
profile_settings,
|
experience_settings,
|
||||||
[
|
[
|
||||||
[[], \"Privacy\", \"title\"],
|
[[], \"Privacy\", \"title\"],
|
||||||
[
|
[
|
||||||
|
@ -1988,6 +1981,10 @@
|
||||||
\"{{ profile.settings.hide_username_badges }}\",
|
\"{{ profile.settings.hide_username_badges }}\",
|
||||||
\"checkbox\",
|
\"checkbox\",
|
||||||
],
|
],
|
||||||
|
[[\"muted\", \"Muted phrases\"], settings.muted.join(\"\\n\"), \"textarea\", {
|
||||||
|
embed_html:
|
||||||
|
'<span class=\"fade\">Muted phrases should all be on new lines.</span>',
|
||||||
|
}],
|
||||||
[[], \"Questions\", \"title\"],
|
[[], \"Questions\", \"title\"],
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
|
@ -2074,8 +2071,36 @@
|
||||||
\"{{ profile.settings.disable_achievements }}\",
|
\"{{ profile.settings.disable_achievements }}\",
|
||||||
\"checkbox\",
|
\"checkbox\",
|
||||||
],
|
],
|
||||||
|
[[], \"Accessibility\", \"title\"],
|
||||||
|
[
|
||||||
|
[\"large_text\", \"Increase UI text size\"],
|
||||||
|
\"{{ profile.settings.large_text }}\",
|
||||||
|
\"checkbox\",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[\"use_system_font\", \"Always use system font instead\"],
|
||||||
|
\"{{ profile.settings.use_system_font }}\",
|
||||||
|
\"checkbox\",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[\"paged_timelines\", \"Make timelines paged instead of infinitely scrolled\"],
|
||||||
|
\"{{ profile.settings.paged_timelines }}\",
|
||||||
|
\"checkbox\",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[\"auto_clear_notifs\", \"Automatically clear all notifications when you open the notifications page\"],
|
||||||
|
\"{{ profile.settings.auto_clear_notifs }}\",
|
||||||
|
\"checkbox\",
|
||||||
|
],
|
||||||
],
|
],
|
||||||
settings,
|
settings,
|
||||||
|
{
|
||||||
|
muted: (new_muted) => {
|
||||||
|
settings.muted = new_muted
|
||||||
|
.split(\"\\n\")
|
||||||
|
.map((t) => t.trim());
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const can_use_custom_css =
|
const can_use_custom_css =
|
||||||
|
@ -2351,5 +2376,40 @@
|
||||||
anchor.click();
|
anchor.click();
|
||||||
anchor.remove();
|
anchor.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// links
|
||||||
|
function render_links() {
|
||||||
|
document.getElementById(\"user_links\").innerHTML = \"\";
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for (const link of settings.links) {
|
||||||
|
document.getElementById(\"user_links\").innerHTML += `<li id=\"link_${i}\"><span>${link[0]}</span> (<a href=\"${link[1]}\">${link[1]}</a>) (<a class=\"red\" href=\"javascript:remove_link(${i})\">delete</a>)</li>`;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.add_link = async () => {
|
||||||
|
const label = await trigger(\"atto::prompt\", [\"Link label:\"]);
|
||||||
|
|
||||||
|
if (!label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = await trigger(\"atto::prompt\", [\"Link URL:\"]);
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.links.push([label, url]);
|
||||||
|
render_links();
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.remove_link = (idx) => {
|
||||||
|
settings.links.splice(idx, 1);
|
||||||
|
document.getElementById(`link_${idx}`).remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
render_links();
|
||||||
});"))))
|
});"))))
|
||||||
(text "{% endblock %}")
|
(text "{% endblock %}")
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
(div
|
(div
|
||||||
("class" "card small flex items_center gap_2")
|
("class" "card small flex items_center gap_2")
|
||||||
(text "{{ components::avatar(id=add_user.username, size=\"24px\") }}")
|
(text "{{ components::avatar(id=add_user.id, size=\"24px\") }}")
|
||||||
(text "{{ components::full_username(user=add_user) }}"))
|
(text "{{ components::full_username(user=add_user) }}"))
|
||||||
(div
|
(div
|
||||||
("class" "card flex flex_col gap_2")
|
("class" "card flex flex_col gap_2")
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
(button
|
(button
|
||||||
(icon (text "check"))
|
(icon (text "check"))
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"general:action.save\" }}"))))))
|
(str (text "general:action.save")))))))
|
||||||
(text "{% if not stack.is_locked -%}")
|
(text "{% if not stack.is_locked -%}")
|
||||||
(div
|
(div
|
||||||
("class" "card_nest")
|
("class" "card_nest")
|
||||||
|
|
|
@ -153,28 +153,8 @@ pub async fn update_user_settings_request(
|
||||||
}
|
}
|
||||||
|
|
||||||
// check lengths
|
// check lengths
|
||||||
if req.display_name.len() > 32 {
|
if let Err(e) = req.verify_values() {
|
||||||
return Json(Error::DataTooLong("display name".to_string()).into());
|
return Json(e.into());
|
||||||
}
|
|
||||||
|
|
||||||
if req.warning.len() > 2048 {
|
|
||||||
return Json(Error::DataTooLong("warning".to_string()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.status.len() > 256 {
|
|
||||||
return Json(Error::DataTooLong("status".to_string()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.biography.len() > 4096 {
|
|
||||||
return Json(Error::DataTooLong("warning".to_string()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.mail_signature.len() > 2048 {
|
|
||||||
return Json(Error::DataTooLong("mail signature".to_string()).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.forum_signature.len() > 2048 {
|
|
||||||
return Json(Error::DataTooLong("forum signature".to_string()).into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check percentage themes
|
// check percentage themes
|
||||||
|
|
|
@ -50,3 +50,4 @@ oiseau = { version = "0.1.2", default-features = false, features = [
|
||||||
paste = { version = "1.0.15", optional = true }
|
paste = { version = "1.0.15", optional = true }
|
||||||
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] }
|
||||||
buckets-core = "1.0.4"
|
buckets-core = "1.0.4"
|
||||||
|
serde_valid = "1.0.5"
|
||||||
|
|
|
@ -709,7 +709,8 @@ impl DataManager {
|
||||||
|
|
||||||
self.cache_clear_user(&other_user).await;
|
self.cache_clear_user(&other_user).await;
|
||||||
|
|
||||||
// create audit log entry
|
// create audit log entry (if we aren't the user that is being updated)
|
||||||
|
if user.id != other_user.id {
|
||||||
self.create_audit_log_entry(AuditLogEntry::new(
|
self.create_audit_log_entry(AuditLogEntry::new(
|
||||||
user.id,
|
user.id,
|
||||||
format!(
|
format!(
|
||||||
|
@ -718,6 +719,7 @@ impl DataManager {
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use crate::model::{Error, Result};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
oauth::AuthGrant,
|
oauth::AuthGrant,
|
||||||
permissions::{FinePermission, SecondaryPermission},
|
permissions::{FinePermission, SecondaryPermission},
|
||||||
|
@ -10,6 +12,7 @@ use tetratto_shared::{
|
||||||
snow::Snowflake,
|
snow::Snowflake,
|
||||||
unix_epoch_timestamp,
|
unix_epoch_timestamp,
|
||||||
};
|
};
|
||||||
|
use serde_valid::Validate;
|
||||||
|
|
||||||
/// `(ip, token, creation timestamp)`
|
/// `(ip, token, creation timestamp)`
|
||||||
pub type Token = (String, String, usize);
|
pub type Token = (String, String, usize);
|
||||||
|
@ -187,13 +190,16 @@ impl Default for DefaultProfileTabChoice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Default, Validate)]
|
||||||
pub struct UserSettings {
|
pub struct UserSettings {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[validate(max_length = 32)]
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[validate(max_length = 4096)]
|
||||||
pub biography: String,
|
pub biography: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[validate(max_length = 2048)]
|
||||||
pub warning: String,
|
pub warning: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub private_profile: bool,
|
pub private_profile: bool,
|
||||||
|
@ -303,6 +309,7 @@ pub struct UserSettings {
|
||||||
pub private_mails: bool,
|
pub private_mails: bool,
|
||||||
/// The user's status. Shows over connection info.
|
/// The user's status. Shows over connection info.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[validate(max_length = 256)]
|
||||||
pub status: String,
|
pub status: String,
|
||||||
/// The mime type of the user's banner.
|
/// The mime type of the user's banner.
|
||||||
#[serde(default = "mime_avif")]
|
#[serde(default = "mime_avif")]
|
||||||
|
@ -365,9 +372,11 @@ pub struct UserSettings {
|
||||||
pub hide_social_follows: bool,
|
pub hide_social_follows: bool,
|
||||||
/// The signature automatically attached to new mail letters.
|
/// The signature automatically attached to new mail letters.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[validate(max_length = 2048)]
|
||||||
pub mail_signature: String,
|
pub mail_signature: String,
|
||||||
/// The signature automatically attached to new forum posts.
|
/// The signature automatically attached to new forum posts.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
#[validate(max_length = 2048)]
|
||||||
pub forum_signature: String,
|
pub forum_signature: String,
|
||||||
/// If coin transfer requests are disabled.
|
/// If coin transfer requests are disabled.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -381,6 +390,26 @@ pub struct UserSettings {
|
||||||
/// If the user's system font is always used over Lexend.
|
/// If the user's system font is always used over Lexend.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub use_system_font: bool,
|
pub use_system_font: bool,
|
||||||
|
/// The user's location. This isn't actually verified or anything, so it can really
|
||||||
|
/// be whatever the user wants.
|
||||||
|
#[serde(default)]
|
||||||
|
#[validate(max_length = 128)]
|
||||||
|
pub location: String,
|
||||||
|
/// External links for the user's other profiles on other websites.
|
||||||
|
#[serde(default)]
|
||||||
|
#[validate(max_items = 5)]
|
||||||
|
#[validate(unique_items)]
|
||||||
|
pub links: Vec<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserSettings {
|
||||||
|
pub fn verify_values(&self) -> Result<()> {
|
||||||
|
if let Err(e) = self.validate() {
|
||||||
|
return Err(Error::MiscError(e.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mime_avif() -> String {
|
fn mime_avif() -> String {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue