From b872cb8db888dbbbcca426b19f6a67f37873f9bb Mon Sep 17 00:00:00 2001 From: Luna D Date: Thu, 23 Nov 2023 17:07:49 +0100 Subject: [PATCH 01/65] restart redesign from latest master --- assets/css/application.scss | 51 ++ assets/css/common/_barline.scss | 13 - assets/css/common/_base.scss | 503 ------------------ assets/css/common/_blocks.scss | 245 --------- assets/css/common/_burger.scss | 38 -- assets/css/common/_buttons.scss | 102 ---- assets/css/common/_dimensions.scss | 39 -- assets/css/common/_dropdown.scss | 33 -- assets/css/common/_forms.scss | 182 ------- assets/css/common/_global.scss | 63 --- assets/css/common/_header.scss | 280 ---------- assets/css/common/_layout.scss | 420 --------------- assets/css/common/_measurements.scss | 67 +++ assets/css/common/_media.scss | 114 ---- assets/css/common/_mixins.scss | 58 ++ assets/css/common/_shame.scss | 6 - assets/css/common/_text.scss | 7 - assets/css/elements/_avatar.scss | 4 + assets/css/elements/_base.scss | 34 ++ assets/css/elements/_block.scss | 139 +++++ assets/css/elements/_button.scss | 117 ++++ assets/css/elements/_dropdown.scss | 69 +++ assets/css/elements/_flash.scss | 52 ++ assets/css/elements/_flex.scss | 90 ++++ assets/css/elements/_heading.scss | 38 ++ assets/css/elements/_input.scss | 26 + assets/css/elements/_interaction.scss | 34 ++ assets/css/elements/_layout.scss | 89 ++++ assets/css/elements/_media.scss | 135 +++++ assets/css/elements/_mobile.scss | 27 + assets/css/elements/_separator.scss | 39 ++ assets/css/elements/_table.scss | 14 + assets/css/themes/dark-blue.scss | 92 ++++ assets/css/themes/dark-purple.scss | 92 ++++ assets/css/themes/dark.scss | 193 ------- assets/css/themes/default.scss | 183 ------- assets/css/themes/red.scss | 195 ------- assets/css/views/_admin.scss | 12 + assets/css/views/_adverts.scss | 30 -- assets/css/views/_approval.scss | 2 +- assets/css/views/_badges.scss | 15 - assets/css/views/_burger.scss | 4 + assets/css/views/_channels.scss | 21 - assets/css/views/_comments.scss | 17 - assets/css/views/_commissions.scss | 38 -- assets/css/views/_communication.scss | 45 ++ assets/css/views/_communications.scss | 104 ---- assets/css/views/_duplicate_reports.scss | 53 -- assets/css/views/_filters.scss | 31 -- assets/css/views/_galleries.scss | 67 --- assets/css/views/_header.scss | 95 ++++ assets/css/views/_images.scss | 331 ------------ assets/css/views/_markdown.scss | 31 ++ assets/css/views/_metabar.scss | 66 +++ assets/css/views/_pages.scss | 10 - assets/css/views/_pagination.scss | 50 +- assets/css/views/_polls.scss | 129 ----- assets/css/views/_posts.scss | 15 - assets/css/views/_profiles.scss | 131 ----- assets/css/views/_search.scss | 3 - assets/css/views/_statistics.scss | 31 ++ assets/css/views/_stats.scss | 7 - assets/css/views/_tag.scss | 88 +++ assets/css/views/_tags.scss | 217 -------- assets/js/app.js | 15 +- assets/js/galleries.js | 2 +- assets/js/resizablemedia.js | 71 --- assets/js/vendor/closest.polyfill.js | 31 -- assets/js/vendor/customevent.polyfill.js | 17 - assets/js/vendor/es6.polyfill.js | 104 ---- assets/js/vendor/fetch.polyfill.js | 53 -- assets/js/vendor/promise.polyfill.js | 225 -------- assets/js/vendor/values-entries.polyfill.js | 17 - assets/js/when-ready.js | 8 +- assets/webpack.config.js | 5 +- .../admin/report/claim_controller.ex | 2 +- .../admin/user/api_key_controller.ex | 2 +- .../admin/user/downvote_controller.ex | 2 +- .../controllers/admin/user/vote_controller.ex | 2 +- .../controllers/admin/user/wipe_controller.ex | 2 +- .../controllers/confirmation_controller.ex | 4 +- .../duplicate_report/accept_controller.ex | 2 +- .../accept_reverse_controller.ex | 2 +- .../controllers/filter/current_controller.ex | 2 +- .../controllers/filter_controller.ex | 2 +- .../controllers/image/feature_controller.ex | 2 +- .../controllers/image/file_controller.ex | 2 +- .../controllers/image_controller.ex | 2 +- .../controllers/password_controller.ex | 4 +- .../profile/artist_link_controller.ex | 2 +- .../profile/commission/item_controller.ex | 2 +- .../profile/commission_controller.ex | 2 +- .../registration/email_controller.ex | 4 +- .../controllers/report_controller.ex | 4 +- .../controllers/tag_controller.ex | 2 +- .../controllers/topic/poll/vote_controller.ex | 2 +- .../controllers/topic/post_controller.ex | 2 +- .../controllers/unlock_controller.ex | 4 +- lib/philomena_web/plugs/check_captcha_plug.ex | 2 +- .../plugs/compromised_password_check_plug.ex | 2 +- .../plugs/ensure_user_enabled_plug.ex | 2 +- .../plugs/not_authorized_plug.ex | 2 +- lib/philomena_web/plugs/not_found_plug.ex | 2 +- lib/philomena_web/plugs/require_user_plug.ex | 2 +- .../activity/_comment_strip.html.slime | 2 +- .../templates/activity/index.html.slime | 54 +- .../admin/approval/_approvals.html.slime | 2 +- .../admin/badge/user/index.html.slime | 2 +- .../admin/dnp_entry/index.html.slime | 2 +- .../templates/admin/mod_note/index.html.slime | 2 +- .../admin/report/_reports.html.slime | 4 +- .../templates/admin/report/index.html.slime | 6 +- .../templates/admin/report/show.html.slime | 2 +- .../templates/admin/user/_form.html.slime | 8 +- .../templates/admin/user/index.html.slime | 2 +- .../templates/ban/_ban_reason.html.slime | 2 +- .../templates/ban/_bans.html.slime | 4 +- .../templates/channel/_channel_box.html.slime | 2 +- .../templates/channel/_form.html.slime | 2 +- .../subscription/_subscription.html.slime | 6 +- .../templates/comment/_comment.html.slime | 2 +- .../comment/_comment_with_image.html.slime | 2 +- .../templates/comment/index.html.slime | 4 +- .../commission/_directory_results.html.slime | 6 +- .../commission/_directory_sidebar.html.slime | 2 +- .../templates/conversation/index.html.slime | 2 +- .../templates/conversation/new.html.slime | 2 +- .../templates/conversation/show.html.slime | 2 +- .../templates/dnp_entry/_form.html.slime | 2 +- .../templates/dnp_entry/index.html.slime | 6 +- .../templates/dnp_entry/show.html.slime | 4 +- .../duplicate_report/index.html.slime | 2 +- .../templates/filter/index.html.slime | 4 +- .../tag_change/index.html.slime | 2 +- .../templates/forum/index.html.slime | 8 +- .../templates/forum/show.html.slime | 8 +- .../subscription/_subscription.html.slime | 6 +- .../templates/gallery/index.html.slime | 2 +- .../templates/gallery/show.html.slime | 20 +- .../subscription/_subscription.html.slime | 6 +- .../image/_add_to_gallery_dropdown.html.slime | 10 +- .../image/_deleted_toggle.html.slime | 51 +- .../templates/image/_hidden_toggle.html.slime | 4 +- .../image/_image_approval_banner.html.slime | 2 +- .../templates/image/_image_box.html.slime | 32 +- .../image/_image_container.html.slime | 15 +- .../templates/image/_image_meta.html.slime | 30 +- .../_image_mobile_information.html.slime | 43 ++ .../image/_image_mobile_navigation.html.slime | 10 + .../templates/image/_image_page.html.slime | 4 +- .../templates/image/_quick_tag.html.slime | 8 +- .../templates/image/_random_button.html.slime | 2 +- .../templates/image/_tags.html.slime | 2 +- .../image/_uploader_mobile.html.slime | 19 + .../templates/image/comment/_form.html.slime | 5 +- .../image/comment/history/index.html.slime | 2 +- .../templates/image/comment/index.html.slime | 4 +- .../templates/image/index.html.slime | 41 +- .../templates/image/related/index.html.slime | 4 +- .../templates/image/show.html.slime | 3 + .../subscription/_subscription.html.slime | 12 +- .../image/tag_change/index.html.slime | 2 +- .../ip_profile/tag_change/index.html.slime | 2 +- .../layout/_flash_warnings.html.slime | 52 +- .../templates/layout/_header.html.slime | 145 +++-- .../layout/_header_navigation.html.slime | 34 +- .../layout/_header_staff_links.html.slime | 99 ++-- .../templates/layout/app.html.slime | 3 +- .../templates/markdown/_toolbar.html.slime | 2 + .../templates/message/_message.html.slime | 2 +- .../notification/_notification.html.slime | 2 +- .../templates/notification/index.html.slime | 5 +- .../pagination/_pagination.html.slime | 74 ++- .../templates/post/_post.html.slime | 2 +- .../templates/post/_post_options.html.slime | 2 +- .../templates/post/index.html.slime | 4 +- .../templates/post/preview/create.html.slime | 2 +- .../templates/profile/_about_me.html.slime | 17 +- .../templates/profile/_admin_block.html.slime | 136 +++-- .../profile/_recent_comments.html.slime | 2 +- .../profile/_recent_galleries.html.slime | 6 +- .../profile/_recent_images.html.slime | 13 +- .../profile/_recent_posts.html.slime | 8 +- .../templates/profile/_statistics.html.slime | 52 +- .../profile/artist_link/_form.html.slime | 2 +- .../commission/_listing_items.html.slime | 2 +- .../commission/_listing_sheet.html.slime | 2 +- .../commission/_listing_sidebar.html.slime | 16 +- .../profile/description/edit.html.slime | 6 +- .../profile/scratchpad/edit.html.slime | 4 +- .../templates/profile/show.html.slime | 63 ++- .../profile/tag_change/index.html.slime | 2 +- .../templates/report/index.html.slime | 2 +- .../templates/search/reverse/index.html.slime | 4 +- .../templates/staff/index.html.slime | 2 +- .../templates/stat/index.html.slime | 4 +- .../templates/tag/_quick_tag_table.html.slime | 4 +- .../templates/tag/_tag_info_row.html.slime | 22 +- .../templates/tag/image/edit.html.slime | 2 +- .../templates/tag/index.html.slime | 4 +- .../templates/tag/tag_change/index.html.slime | 2 +- .../topic/post/history/index.html.slime | 2 +- .../templates/topic/show.html.slime | 2 +- .../subscription/_subscription.html.slime | 6 +- lib/philomena_web/user_auth.ex | 2 +- lib/philomena_web/views/layout_view.ex | 14 +- priv/repo/seeds_development.json | 345 +++++++++++- 207 files changed, 2850 insertions(+), 4969 deletions(-) create mode 100644 assets/css/application.scss delete mode 100644 assets/css/common/_barline.scss delete mode 100644 assets/css/common/_base.scss delete mode 100644 assets/css/common/_blocks.scss delete mode 100644 assets/css/common/_burger.scss delete mode 100644 assets/css/common/_buttons.scss delete mode 100644 assets/css/common/_dimensions.scss delete mode 100644 assets/css/common/_dropdown.scss delete mode 100644 assets/css/common/_forms.scss delete mode 100644 assets/css/common/_global.scss delete mode 100644 assets/css/common/_header.scss delete mode 100644 assets/css/common/_layout.scss create mode 100644 assets/css/common/_measurements.scss delete mode 100644 assets/css/common/_media.scss create mode 100644 assets/css/common/_mixins.scss delete mode 100644 assets/css/common/_shame.scss delete mode 100644 assets/css/common/_text.scss create mode 100644 assets/css/elements/_avatar.scss create mode 100644 assets/css/elements/_base.scss create mode 100644 assets/css/elements/_block.scss create mode 100644 assets/css/elements/_button.scss create mode 100644 assets/css/elements/_dropdown.scss create mode 100644 assets/css/elements/_flash.scss create mode 100644 assets/css/elements/_flex.scss create mode 100644 assets/css/elements/_heading.scss create mode 100644 assets/css/elements/_input.scss create mode 100644 assets/css/elements/_interaction.scss create mode 100644 assets/css/elements/_layout.scss create mode 100644 assets/css/elements/_media.scss create mode 100644 assets/css/elements/_mobile.scss create mode 100644 assets/css/elements/_separator.scss create mode 100644 assets/css/elements/_table.scss create mode 100644 assets/css/themes/dark-blue.scss create mode 100644 assets/css/themes/dark-purple.scss delete mode 100644 assets/css/themes/dark.scss delete mode 100644 assets/css/themes/default.scss delete mode 100644 assets/css/themes/red.scss create mode 100644 assets/css/views/_admin.scss delete mode 100644 assets/css/views/_adverts.scss delete mode 100644 assets/css/views/_badges.scss create mode 100644 assets/css/views/_burger.scss delete mode 100644 assets/css/views/_channels.scss delete mode 100644 assets/css/views/_comments.scss delete mode 100644 assets/css/views/_commissions.scss create mode 100644 assets/css/views/_communication.scss delete mode 100644 assets/css/views/_communications.scss delete mode 100644 assets/css/views/_duplicate_reports.scss delete mode 100644 assets/css/views/_filters.scss delete mode 100644 assets/css/views/_galleries.scss create mode 100644 assets/css/views/_header.scss delete mode 100644 assets/css/views/_images.scss create mode 100644 assets/css/views/_markdown.scss create mode 100644 assets/css/views/_metabar.scss delete mode 100644 assets/css/views/_pages.scss delete mode 100644 assets/css/views/_polls.scss delete mode 100644 assets/css/views/_posts.scss delete mode 100644 assets/css/views/_profiles.scss delete mode 100644 assets/css/views/_search.scss create mode 100644 assets/css/views/_statistics.scss delete mode 100644 assets/css/views/_stats.scss create mode 100644 assets/css/views/_tag.scss delete mode 100644 assets/css/views/_tags.scss delete mode 100644 assets/js/resizablemedia.js delete mode 100644 assets/js/vendor/closest.polyfill.js delete mode 100644 assets/js/vendor/customevent.polyfill.js delete mode 100644 assets/js/vendor/es6.polyfill.js delete mode 100644 assets/js/vendor/fetch.polyfill.js delete mode 100644 assets/js/vendor/promise.polyfill.js delete mode 100644 assets/js/vendor/values-entries.polyfill.js create mode 100644 lib/philomena_web/templates/image/_image_mobile_information.html.slime create mode 100644 lib/philomena_web/templates/image/_image_mobile_navigation.html.slime create mode 100644 lib/philomena_web/templates/image/_uploader_mobile.html.slime diff --git a/assets/css/application.scss b/assets/css/application.scss new file mode 100644 index 000000000..2f1f44d33 --- /dev/null +++ b/assets/css/application.scss @@ -0,0 +1,51 @@ +/* + * Main stylesheet file. + * + * This generates the base file for the stylesheets used throughout the project. + * Themes should be contained within the "themes" folder, and only contain + * color variations, and optional CSS changes. + * + */ + +$fa-font-path: '~@fortawesome/fontawesome-free/webfonts'; + +@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss"; +@import "~@fortawesome/fontawesome-free/scss/solid.scss"; +@import "~@fortawesome/fontawesome-free/scss/regular.scss"; +@import "~@fortawesome/fontawesome-free/scss/brands.scss"; +@import "~normalize-scss/sass/normalize/import-now"; + +// Import the default theme to act as a fallback. +@import "~themes/dark-blue"; + +// Files containing common properties, such as variable definitions. +@import "~common/measurements"; +@import "~common/mixins"; + +// General style elements that are used throughout the project. +@import "~elements/avatar"; +@import "~elements/base"; +@import "~elements/block"; +@import "~elements/button"; +@import "~elements/dropdown"; +@import "~elements/flash"; +@import "~elements/flex"; +@import "~elements/heading"; +@import "~elements/input"; +@import "~elements/interaction"; +@import "~elements/layout"; +@import "~elements/media"; +@import "~elements/mobile"; +@import "~elements/separator"; +@import "~elements/table"; + +// Style elements specific to certain pages. +@import "~views/admin"; +@import "~views/burger"; +@import "~views/communication"; +@import "~views/header"; +@import "~views/markdown"; +@import "~views/metabar"; +@import "~views/pagination"; +@import "~views/statistics"; +@import "~views/tag"; diff --git a/assets/css/common/_barline.scss b/assets/css/common/_barline.scss deleted file mode 100644 index 58b4292b0..000000000 --- a/assets/css/common/_barline.scss +++ /dev/null @@ -1,13 +0,0 @@ -.sparkline { - display: flex; - height: 20px; - border-bottom: 1px solid $sparkline_color; -} - -.barline__bar { - fill: $sparkline_color; -} - -.barline__bar:hover { - fill: red; -} diff --git a/assets/css/common/_base.scss b/assets/css/common/_base.scss deleted file mode 100644 index b5375e072..000000000 --- a/assets/css/common/_base.scss +++ /dev/null @@ -1,503 +0,0 @@ -/* - * This file contains base styles that are overridden by custom stylesheets. These stylesheets need - * to be placed in this directory, and they are imported at the bottom of the file. - * - * Define rules that are not supposed to be overridden after the @imports at the bottom of the file. - */ - -@import "dimensions"; -@import "global"; - -// Because FA is a SPECIAL SNOWFLAKE. -$fa-font-path: '~@fortawesome/fontawesome-free/webfonts'; - -@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss"; -@import "~@fortawesome/fontawesome-free/scss/solid.scss"; -@import "~@fortawesome/fontawesome-free/scss/regular.scss"; -@import "~@fortawesome/fontawesome-free/scss/brands.scss"; -@import "~normalize-scss/sass/normalize/import-now"; - -body { - background-color: $background_color; - color: $foreground_color; - font-family: $font_family_base; - font-size: 13px; - margin: 0; - padding: 0; - line-height: 1.15; -} - -/* normalize.css breakage */ -sup, sub { - line-height: 1.15em; -} - -sub { - bottom: -0.15em; -} - -h1, h2, h3, h4, h5 { - font-weight: normal; - text-rendering: optimizeLegibility; - margin-bottom: 0.62em; - line-height: 1.15em; -} - -h1 { font-size: 24px; } - -h2 { font-size: 22px; } - -h3 { font-size: 19px; } - -h4 { - font-size: 17px; - margin-top: 1em; - margin-bottom: 0.5em; -} - -// Slightly dirty, no clean way to strip top margins off headers though -.remove-top-margin { - margin-top: 0; -} - -h5 { - margin-top: 0.5em; - margin-bottom: 0.3em; - font-size: 15px; -} - -h6 { - font-size: 10px; - margin: 2px; - margin-bottom: 0; -} - -.paragraph { - hyphens: none; - margin-bottom: var(--padding-small); - margin-top: var(--padding-small); - margin-left: 2px; - line-height: 1.35em; -} - -.communication__body__text .paragraph { - margin-bottom: var(--padding-normal); -} - -.paragraph img, .communication__body__text img, .block__content img { - max-width: 100%; -} - -p { - @extend .paragraph; -} - -pre { - background-color: $meta_color; - padding: 10px; - font-size: 11px; - font-family: $font_family_monospace; - border: 1px solid $meta_border_color; - max-width: 100%; - overflow-y: hidden; - overflow-x: auto; - - code { - border: 0; - background: 0; - padding: 0; - } -} - -a, a:active, a:visited { - color: $link_color; - text-decoration: none; -} - -a:hover { - color: $link_hover_color; -} - -img { - margin: 0; - border: 0; - vertical-align: bottom; -} - -.clearfix { - clear: both; - height: 0; -} - -/* Child selectors, as opposed to flat BEM classes, are justified in this case. - * HTML tables are bulky to begin with; adding .table__row and .table__cell to every - * item will hinder the development speed and decrease readability. */ -.table { - padding-left: 10px; - width: 100%; - border-collapse: collapse; - border: $border; - - th { - font-weight: bold; - text-align: left; - padding: 5px; - &.center { - text-align: center; - } - } - - > thead > tr { - background: $background_color; - } - - > tbody { - border: $border; - > tr { - @include even-odd; - } - } - - td { - padding: 5px; - } -} - -.table__tiny-column { - width: 50px; -} - -.border-vertical { - border-top: 2px solid $border_color; -} - -.background-success { - background-color: $success_light_color; -} - -.background-warning { - background-color: $warning_light_color; -} - -.background-danger { - background-color: $danger_light_color; -} - -hr { - border: 0; - height: 1px; - background: $border_color; - margin: var(--padding-small); -} - -#error_explanation { - margin: 1em 0; - background: $warning_light_color; - border: 1px solid $warning_color; - padding: 0.62em; - li { - margin: 3px; - margin-left: 15px; - } - h2 { - font-size: 19px; - } -} - -//rules page. API page also borrows stuff from here -.rule { - padding: 5px; - h2 { - background: $background_odd_color; - border: $border; - padding: 5px; - padding-bottom: 7px; - } - h2.important { - background: $danger_light_color; - border-color: $danger_color; - } - // Make HTML lists with multi-line text readable - ul { - margin: 0; - padding: 5px 20px; - } - li { - padding: 5px 0; - } -} - -.walloftext { - font-size: 14px; - - @media (max-width: 900px) { - width: 95%; - } -} - -// Text Editor -blockquote { - margin: 1em 2em; - border: 1px dotted $foreground_color; - padding: var(--padding-small); - background-color: inherit; -} - -blockquote .paragraph { - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } -} - -// Prevent blockquote from gaining far too much indentation and breaking. -blockquote blockquote blockquote blockquote blockquote blockquote { - margin: 1em 0; - padding: 1em 2px; -} - -// Horizontal space is at a high premium on mobile. -@media (max-width: $min_px_width_for_desktop_layout) { - blockquote { - margin: 1em 4px; - padding: var(--padding-small) 4px; - } -} - -.spoiler, .spoiler-revealed { - display: inline-block; - position: relative; -} - -.spoiler::before, .spoiler-revealed::before { - content: ' '; - display: block; - position: absolute; - pointer-events: none; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: $admin_links_hover_color; -} - -.spoiler:hover::before, .spoiler-revealed::before { - display: none; -} - -.spoiler { - background-color: $admin_links_hover_color; - color: $admin_links_hover_color; - - a { - color: $admin_links_hover_color; - } - - code { - background: 0; - border: 0; - } - - &:not(:hover) > .image-show-container { - background: $admin_links_hover_color; - - > * { - visibility: hidden; - } - } -} - -.spoiler:hover, .spoiler-revealed { - background-color: $admin_links_color; - color: $foreground_color; - a { - color: $link_color; - } - a:hover { - color: $link_dark_color; - } - code { - @extend code; - } -} - -.literal { - white-space: pre-wrap; -} - -.editor-syntax-reference { - margin-bottom: 6px; -} - -@mixin image-alt-size($name, $size) { - @media (min-width: $size) { - img[alt=#{$name}] { - max-height: $size !important; - max-width: $size !important; - } - } -} - -@include image-alt-size(tiny, $image_tiny_size); -@include image-alt-size(small, $image_small_size); -@include image-alt-size(medium, $image_medium_size); -@include image-alt-size(large, $image_large_size); - -table { - @extend .table; -} - -code { - background-color: $meta_color; - border: 1px solid $meta_border_color; - padding: 0 4px; -} - -//donations -.donate-button { - background: 0; - border: 0; - margin-left: 170px; -} - -.button_to { - display: inline; -} - -/* User titles (admin/mod/assistant) and channel states (live/off air) */ -.label { - padding: 6px; - overflow-y: hidden; -} - -.label--small { - font-size: 9px; - overflow: hidden; -} - -.label--block { - display: inline-block; - margin: 2px 6px 6px 0; -} - -.label--narrow { - padding: 0 2px; -} - -.label--success { - background: $success_light_color; - border: 1px solid $success_color; -} - -.label--danger { - background: $danger_light_color; - border: 1px solid $danger_color; -} - -.label--warning { - background: $warning_light_color; - border: 1px solid $warning_color; -} - -.label--purple { - background: $assistant_color; - border: 1px solid $assistant_border_color; -} - -.label--primary { - background: $primary_light_color; - border: 1px solid $primary_color; -} - -.large-text { - font-size: 15px; -} - -.small-text { - font-size: 10px; -} - -.page-current { - padding: 0 $header_spacing; -} - -i.favicon-home { - background-image: url("/favicon.svg"); - background-size: contain; - background-repeat: no-repeat; - background-position: center; - min-width: 1em; - height: 1em; - display: inline-block; -} - -// changelog -.commit-sha { - @extend .label--purple; - padding: 2px; - font-size: 12px; - font-family: $font_family_monospace; - vertical-align: top; -} - -.commit-message { - display: block; -} - -span.stat { - font-size: 18px; -} - -.seamless-frame { - border: 0; - overflow: hidden; - padding: 0; -} - -@import "barline"; -@import "blocks"; -@import "burger"; -@import "buttons"; -@import "dropdown"; -@import "forms"; -@import "header"; -@import "layout"; -@import "media"; -@import "shame"; -@import "text"; - -@import "~views/adverts"; -@import "~views/approval"; -@import "~views/badges"; -@import "~views/channels"; -@import "~views/comments"; -@import "~views/commissions"; -@import "~views/communications"; -@import "~views/duplicate_reports"; -@import "~views/filters"; -@import "~views/galleries"; -@import "~views/images"; -@import "~views/pages"; -@import "~views/polls"; -@import "~views/posts"; -@import "~views/profiles"; -@import "~views/pagination"; -@import "~views/search"; -@import "~views/staff"; -@import "~views/stats"; -@import "~views/tags"; - -.no-overflow { - overflow: hidden; -} - -.no-overflow-x { - overflow-x: hidden; -} - -.hidden { - display: none !important; -} diff --git a/assets/css/common/_blocks.scss b/assets/css/common/_blocks.scss deleted file mode 100644 index 883302a8f..000000000 --- a/assets/css/common/_blocks.scss +++ /dev/null @@ -1,245 +0,0 @@ -.block { - margin-bottom: $block_spacing; - .success { - background-color: $success_color; - } - - .warning { - background-color: $warning_color; - } - - .danger { - background-color: $danger_color; - } -} - -.block__content, .block__tab { - border-left: 1px solid $border_color; - border-right: 1px solid $border_color; - background: $background_color; - padding: $block_spacing; -} - -.block__content img, .block__tab img { - max-width: 100%; -} - -.block__content:first-child { - border-top: 1px solid $border_color; -} - -.block__content:last-child, .block__tab:not(.hidden) { - border-bottom: 1px solid $border_color; -} - -.block__content--destroyed { - background-color: $destroyed_content_color !important; -} - -.block__header { - font-size: 14px; - line-height: $block_header_height; - background: $block_header_color; - color: $foreground_color; -} - -//FIXME This whole area has problems with structure, things are too coupled -.block__header--sub { - @extend .block__header; - line-height: $block_header_sub_height; - a { - padding-left: $header_spacing*0.5; - padding-right: $header_spacing*0.5; - } -} - -.block__header--user-credit { - box-sizing: border-box; - line-height: 20px; - padding-left: 12px; - padding-right: 12px; - padding-bottom: 4px; -} - -.block__header--single-item { - @extend .block__header; - display: block; - width: 100%; - box-sizing: border-box; -} - -.block__header--single-item, .block__header__item, .block__header a { - padding-left: $header_spacing; - padding-right: $header_spacing; -} - -a.block__header--single-item, .block__header a { - color: $block_header_link_text_color; - display: inline-block; - cursor: pointer; - font-weight: bold; - &:hover { - background: $block_header_hover_color; - color: $block_header_link_text_hover_color; - } -} - -.block__header a, .header__span { - background: $block_header_color; -} - -.block__header__title { - font-size: 14px; - margin: 0; - padding: 0 ($header_spacing * 0.5) 0 $header_spacing; -} - -.block__header__buttons { - display: flex; - flex-direction: row; - line-height: 100%; - align-items: center; - background: 0; -} - -.block__tagbox { - padding-top: $block_spacing + 5px; -} - -.block__header--light { - background: $block_header_light_color; - a, .header__span { - background: $block_header_light_color; - } - - a { - color: $block_header_light_link_text_color; - &:hover { - background: $block_header_light_hover_color; - color: $block_header_light_link_text_hover_color; - } - } -} - -.block__header--js-tabbed { - @extend .block__header--light; - background: transparent; - - border-bottom: $border; - a { - border-top: $border; - /* hide the bottom border for tab links */ - margin-bottom: -1px; - border-bottom: 1px solid $block_header_light_color; - } - a:hover { - border-color: $block_header_light_hover_color; - } - a:first-child { border-left: $border; } - a:last-child { border-right: $border; } - - a.selected, a.selected:hover { - cursor: default; - color: $foreground_color; - background: $background_color; - border-color: $block_header_light_color; - border-bottom: 1px solid $background_color; - } -} - -/* Fixed blocks do not have header and content */ -.block--fixed { - padding: 6px $header_spacing; - background: $background_color; - border: $border; - line-height: 1.35; -} - -.block--fixed--sub { - padding: 3px $header_spacing; -} - -.block--no-margin { - margin: 0; -} - -.block--success { - background: $success_light_color; - border-color: $success_color; -} - -.block--warning { - background: $warning_light_color; - border-color: $warning_color; -} - -.block--danger { - background: $danger_light_color; - border-color: $danger_color; -} - -.block--primary { - background: $primary_light_color; - border-color: $primary_color; -} - -.block--assistant { - background: $assistant_color; - border-color: $assistant_border_color; -} - -/* Somewhat dirty workaround for margins */ - -.block__content, .block__tab, .block--fixed { - > h1, > h2, > h3, > h4, > h5, > h6 { - margin-top: 6px; - } -} - -.block__header__dropdown-tab:hover > a { - background-color: $block_header_hover_color; - color: $block_header_link_text_hover_color; -} - -/* Table-like lists */ -.block__list { - list-style: none; - padding: 0; - margin: 0; - border-bottom: $border; - - &:not(:last-child) { margin-bottom: 6px; } - - a.block__list__link { - width: 100%; - display: block; - box-sizing: border-box; - border: $border; - margin-bottom: -1px; /* collapse borders */ - - background: $background_color; - } - - a.block__list__link.primary { - background: $primary_light_color; - border-color: $primary_color; - } - - a.block__list__link:hover, a.block__list__link.active { - background: $success_light_color; - border-color: $success_color; - } - - a.block__list__link.active:hover { - background: $danger_light_color; - border-color: $danger_color; - } - - a.block__list__link.hidden { - display: none; - } -} - -.block__content--top-border { - border-top: $border; -} diff --git a/assets/css/common/_burger.scss b/assets/css/common/_burger.scss deleted file mode 100644 index f23c8224b..000000000 --- a/assets/css/common/_burger.scss +++ /dev/null @@ -1,38 +0,0 @@ -@mixin transform-n-animation($type, $dur, $delta) { - transform: $delta; - animation: $type $dur ease-in-out; -} - -#burger.open { - display: block !important; - @include transform-n-animation(slidein, 0.4s, translate(0, 0)); -} - -#burger.close { - display: none; -} - -// content sliding open -#container.open { - @include transform-n-animation(open, 0.4s, translate(210px, 0)); -} - -@keyframes open { - 0.00% { transform: translate(0, 0); } - 100% { transform: translate(210px, 0); } -} - -// content closing -#container.close { - animation: close 0.3s ease-in-out; -} - -@keyframes close { - 0.00% { transform: translate(210px, 0); } - 100% { transform: translate(0, 0); } -} - -@keyframes slidein { - 0.00% { transform: translate(-200px, 0); } - 100% { transform: translate(0, 0); } -} diff --git a/assets/css/common/_buttons.scss b/assets/css/common/_buttons.scss deleted file mode 100644 index 7f4da42f0..000000000 --- a/assets/css/common/_buttons.scss +++ /dev/null @@ -1,102 +0,0 @@ -.button { - background-color: $button_background_color; - border: 1px solid $button_border_color; - box-sizing: border-box; - color: $button_text_color; - display: inline-block; - line-height: normal; - font-family: $font_family_base; - font-size: 14px; - padding: 3px 5px; - border-radius: 0; /* reset rounded borders on iOS/Safari */ - cursor: pointer; - &:hover { - background-color: $button_hover_background_color; - border-color: $button_hover_border_color; - } - &:active, &:hover, &:visited { - color: $button_text_color; - } - &:disabled { - opacity: 0.7; - pointer-events: none; - } -} - -.button--inline { - padding: 0.25rem; - border: 0; - border-radius: 0.25rem; - font-weight: 100; -} - -.button--bold { - font-weight: bold; -} - -.button--full-width { - width: 100%; -} - -.button--link { - color: $link_color; - cursor: pointer; - &:hover { - color: $link_hover_color; - } - &:visited { - color: $link_color; - } -} - -.button--separate-left { - margin-left: 5px; -} - -.button--separate-right { - margin-right: 5px; -} - -.button--small { - padding: 0 3px; -} - -.button--state-primary { - background-color: $button_primary_background_color; - border-color: $button_primary_border_color; - &:hover { - background-color: $button_primary_hover_background; - border-color: $button_primary_hover_border; - } -} - -.button--state-success { - background-color: $button_success_background_color; - border-color: $button_success_border_color; - &:hover { - background-color: $button_success_hover_background; - border-color: $button_success_hover_border; - } -} - -.button--state-warning { - background-color: $button_warning_background_color; - border-color: $button_warning_border_color; - &:hover { - background-color: $button_warning_hover_background; - border-color: $button_warning_hover_border; - } -} - -.button--state-danger { - background-color: $button_danger_background_color; - border-color: $button_danger_border_color; - &:hover { - background-color: $button_danger_hover_background; - border-color: $button_danger_hover_border; - } - - &.button--inline { - color: $button_danger_hover_border !important; - } -} diff --git a/assets/css/common/_dimensions.scss b/assets/css/common/_dimensions.scss deleted file mode 100644 index e8151ba95..000000000 --- a/assets/css/common/_dimensions.scss +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Shared layout properties, not unique to any theme - */ - -$border: 1px solid $border_color; - -$font_family_base: verdana, arial, helvetica, sans-serif; -$font_family_monospace: "Droid Sans Mono", monospace; - -$max_px_width_for_limited_desktop_layout: 1150px; -$min_px_width_for_desktop_layout: 800px; -$min_px_width_for_desktop_thumb: 700px; - -$limited_layout_width: 980px; -$medium_layout_width: 1330px; -$centered_layout_side_margin: 24px; -$layout_side_margin: 12px; - -$image_tiny_size: 64px; -$image_small_size: 128px; -$image_medium_size: 256px; -$image_large_size: 512px; - -$header_height: 36px; -$header_field_height: 28px; -$header_sub_height: 32px; -$header_font_size: 16px; -$header_spacing: 12px; -$header_field_spacing: 8px; -$header_field_vertical_spacing: ($header_height - $header_field_height) * 0.5; -$block_header_height: 32px; -$block_header_sub_height: 26px; -$block_spacing: 6px; -$media_box_header_height: 22px; - -:root { - --padding-small: 0.5em; - --padding-normal: 1em; -} diff --git a/assets/css/common/_dropdown.scss b/assets/css/common/_dropdown.scss deleted file mode 100644 index 30c06a50f..000000000 --- a/assets/css/common/_dropdown.scss +++ /dev/null @@ -1,33 +0,0 @@ -.dropdown { - position: relative; - display: inline-block; -} - -.dropdown .dropdown__content { - position: absolute; - display: none; - - /* align at top left (e.g., tag margins) */ - top: 100%; - left: 0; -} - -.dropdown__content.dropdown__content-right { - left: inherit; - right: 0; -} - -/* Our child should be a block element */ -.dropdown .dropdown__content > *:not(.hidden) { - display: block; - white-space: nowrap; -} - -.dropdown:hover .dropdown__content { - /* show it */ - display: block; - /* dropdowns should be at least as big as the button */ - min-width: 100%; - /* and shouldn't sit below content */ - z-index: 999; -} diff --git a/assets/css/common/_forms.scss b/assets/css/common/_forms.scss deleted file mode 100644 index 51bf249d0..000000000 --- a/assets/css/common/_forms.scss +++ /dev/null @@ -1,182 +0,0 @@ -form p { - margin-left: 0; -} - -.field--block { - display: block; -} - -.field--inline { - display: flex; -} - -.field-error-js { - background: $warning_light_color; - padding: 3px; -} - -.input { - box-sizing: border-box; - padding: 6px; - border: 1px solid $input_border; - font-size: 14px; - font-family: $font_family_monospace; - color: $input_text_color; - background: $input_color; - max-width: 100%; /* prevent resizable inputs from overflowing the container */ -} - -.input::placeholder { - opacity: 0.54; - color: inherit; -} - -.input[required]:invalid { - box-shadow: none; -} - -.input:focus { - outline: none; - border: 1px solid $input_border_active; - background: $input_color_active; -} - -.input--wide { - width: 100%; -} - -.input--short { - max-width: 30em; -} - -.input--small { - padding: 0 3px; -} - -.input--text { - min-height: 8.6em; -} - -.input--separate-left { - margin-left: 5px; -} - -.input--separate-top { - margin-top: 5px; -} - -.input--decoy { - display: inline-block; - width: 0; - height: 0; - border: 0; - padding: 0; - box-sizing: border-box; -} - -.input--resize-vertical { - resize: vertical; -} - -.checkbox { - margin: 0.2em 0 0 0.4em; - padding: 0; -} - -textarea { - height: 4em; -} - -.field, .actions { - margin-bottom: 6px; -} - -.field_with_errors { - background: $danger_color; -} - -span.help-block { - background: $danger_color; - display: block; -} - -/* hform is used for 30em inline 2-element textinput/button forms */ -.hform .field { - display: flex; - max-width: 30em; - width: 100%; -} -/* text input - grow to container size */ -.hform__text { - flex: 1 0 auto; -} -/* button - don't shrink or grow at all */ -.hform__button { - flex: 0 0 auto; - margin-left: 5px; -} - -/* Click-toggleable */ - -.toggle-box { - display: none; -} - -.toggle-box + label { - @extend .button; - position: relative; - > * { display: inline; } -} - -.toggle-box + label::before { - content: ""; - width: 0; - height: 0; - border-style: solid; - display: inline-block; - margin-right: 0.25em; -} - -.toggle-box + label::before { - border-width: 0.5em 0 0.5em 0.9em; - border-color: transparent transparent transparent $foreground_color; -} - -.toggle-box:checked + label::before { - border-width: 0.9em 0.5em 0; - border-color: $foreground_color transparent transparent transparent; -} - -.toggle-box-container { - overflow: hidden; - position: relative; - height: 0; -} - -.toggle-box:checked + label + .toggle-box-container { - height: auto; -} - -.toggle-box:checked + label + .toggle-box-container > .toggle-box-container__content { - margin-top: $block_spacing; -} - -select.input, select.input:focus { - -moz-appearance: none; - appearance: none; - background: linear-gradient(45deg, transparent 50%, $foreground_color 50%) calc(100% - 15px) 12px/5px 5px no-repeat,linear-gradient(135deg, $foreground_color 50%, transparent 50%) calc(100% - 10px) 12px/5px 5px no-repeat; - padding-right: 25px; - - & option { - background-color: $input_color; - } - - &:hover option { - background-color: $input_color_active; - } -} - -select.input:hover, select.input:focus:hover { - cursor: pointer; - background-color: $input_color_active; -} diff --git a/assets/css/common/_global.scss b/assets/css/common/_global.scss deleted file mode 100644 index c23f1d37c..000000000 --- a/assets/css/common/_global.scss +++ /dev/null @@ -1,63 +0,0 @@ -@mixin even-odd { - &:nth-child(odd) { - background: $background_odd_color; - } - &:nth-child(even) { - background: $background_even_color; - } - - /* Used in tag changes, among other things. See the definition of - * _odd_color and _even_color for more information on cross-theme customization */ - - //success bgs - &:nth-child(odd).success, - &:nth-child(odd) .success { - background: $success_odd_color; - } - &:nth-child(even).success, - &:nth-child(even) .success { - background: $success_even_color; - } - &:nth-child(even) &:nth-child(odd).success, - &:nth-child(even) &:nth-child(odd) .success { - background: $success_odd_color; - } - - //warning bgs - &:nth-child(odd).warning, - &:nth-child(odd) .warning { - background: $warning_odd_color; - } - &:nth-child(even).warning, - &:nth-child(even) .warning { - background: $warning_even_color; - } - &:nth-child(even) &:nth-child(odd).warning, - &:nth-child(even) &:nth-child(odd) .warning { - background: $warning_odd_color; - } - - //danger bg - &:nth-child(odd).danger, - &:nth-child(odd) .danger { - background: $danger_odd_color; - } - &:nth-child(even).danger, - &:nth-child(even) .danger { - background: $danger_even_color; - } - &:nth-child(even) &:nth-child(odd).danger, - &:nth-child(even) &:nth-child(odd) .danger { - background: $danger_odd_color; - } -} - -@mixin number-wrap($color) { - display: inline-flex; - padding: 0 6px; - margin-left: 6px; - line-height: inherit; - font-size: 16px; - text-align: center; - background: $color; -} diff --git a/assets/css/common/_header.scss b/assets/css/common/_header.scss deleted file mode 100644 index e79e780a4..000000000 --- a/assets/css/common/_header.scss +++ /dev/null @@ -1,280 +0,0 @@ -.header { - background: $header_color; -} - -.header__force-right { - margin-left: auto; -} - -[data-notification-count="0"] { - display: none !important; -} - -.fa-search-button { - padding: 0 0.5rem; -} - -.fa-unread-icon { - color: $unread_message_color; -} - -// For text preceded by an icon -.fa__text { - padding-left: 6px; -} - -a.header__link { - display: inline-block; - padding: 0 $header_spacing; - line-height: $header_height; - font-size: $header_font_size; - color: $text_light_color; - background: $header_color; - flex-shrink: 0; -} - -a.header__link:hover, .header__dropdown:hover > a { - cursor: pointer; - background-color: $header_hover_color; -} - -.header__input, .header__input:focus { - padding: 0 $header_field_spacing; - border: none; - height: $header_field_height; - - background: $header_field_color; - color: $text_light_color; - -webkit-text-fill-color: $text_light_color; - - text-overflow: ellipsis; - - /* reset rounded borders on iOS/Safari */ - border-radius: 0; -} - -span.header__counter { - @include number-wrap($header_hover_color); -} - -select.header__input, select.header__input:focus { - /* in case of + * + * Example usage: + * + * + */ + +import { $$ } from './utils/dom'; + +function lerp(delta: number, from: number, to: number): number { + if (delta >= 1) { return to; } + else if (delta <= 0) { return from; } + + return from + (to - from) * delta; +} + +function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: string, limitProperty: string) { + const parent = el.parentElement; + + if (!parent) { + return; + } + + let minPos = 0; + let maxPos = 0; + let curValue = 0; + let dragging = false; + + function initVars() { + if (!parent) { return; } + + const rect = parent.getBoundingClientRect(); + + minPos = rect.x; + maxPos = rect.x + rect.width - el.clientWidth; + curValue = Number(dataEl.getAttribute(valueProperty) || '0'); + } + + function clampValue(value: number): number { + const storedValue = Number(dataEl.getAttribute(valueProperty) || '0'); + const limitValue = Number(dataEl.getAttribute(limitProperty) || '0'); + + if (storedValue >= limitValue && value < limitValue) { + return limitValue; + } + else if (storedValue < limitValue && value >= limitValue) { + return limitValue - 1; // Offset by 1 to ensure stored value is less than limit. + } + + return value; + } + + function getMin(): number { + return Number(dataEl.getAttribute('min') || '0'); + } + + function getMax(): number { + return Number(dataEl.getAttribute('max') || '0'); + } + + // Define functions to control the drag behavior of the slider. + function dragMove(e: PointerEvent) { + if (!dragging) { return; } + + e.preventDefault(); + + let desiredPos = e.clientX; + + if (desiredPos > maxPos) { + desiredPos = maxPos; + } + else if (desiredPos < minPos) { + desiredPos = minPos; + } + + curValue = clampValue( + lerp( + (desiredPos - minPos) / (maxPos - minPos), + getMin(), + getMax() + ) + ); + + desiredPos = lerp(curValue / getMax(), minPos, maxPos); + + el.style.left = `${desiredPos}px`; + + dataEl.setAttribute(valueProperty, curValue.toString()); + dataEl.dispatchEvent(new InputEvent('input')); + } + + function dragEnd(e: PointerEvent) { + if (!dragging) { return; } + + e.preventDefault(); + + dataEl.setAttribute(valueProperty, curValue.toString()); + dataEl.dispatchEvent(new InputEvent('input')); + + dragging = false; + } + + function dragBegin(e: PointerEvent) { + if (!parent) { return; } + + e.preventDefault(); + initVars(); + + dragging = true; + } + + // Set initial position; + initVars(); + el.style.left = `${lerp(curValue / getMax(), minPos, maxPos)}px`; + + // Attach event listeners for dragging the head. + el.addEventListener('pointerdown', dragBegin); + window.addEventListener('pointerup', dragEnd); + window.addEventListener('pointermove', dragMove); +} + +function setupSlider(el: HTMLInputElement) { + const parent = el.parentElement; + + if (!parent) { + return; + } + + // Create a bunch of divs for presentation. + const sliderContainer: HTMLDivElement = document.createElement('div'); + const minHead: HTMLDivElement = document.createElement('div'); + const maxHead: HTMLDivElement = document.createElement('div'); + const body: HTMLDivElement = document.createElement('div'); + + // Hide the real input, and add CSS classes to our divs. + el.classList.add('hidden'); + sliderContainer.classList.add('slider'); + minHead.classList.add('slider__head'); + minHead.classList.add('slider__head--min'); + maxHead.classList.add('slider__head'); + maxHead.classList.add('slider__head--max'); + body.classList.add('slider__body'); + + // Insert divs into other divs and subsequently into the document. + sliderContainer.appendChild(body); + sliderContainer.appendChild(minHead); + sliderContainer.appendChild(maxHead); + parent.insertBefore(sliderContainer, el); + + // Setup drag events on head elements. + setupDrag(minHead, el, 'valuemin', 'valuemax'); + setupDrag(maxHead, el, 'valuemax', 'valuemin'); +} + +function setupSliders() { + $$('input[type="dualrange"]').forEach(el => { + setupSlider(el); + }); +} + +export { setupSliders }; diff --git a/assets/js/when-ready.js b/assets/js/when-ready.js index ba2e66406..2eb90aa6c 100644 --- a/assets/js/when-ready.js +++ b/assets/js/when-ready.js @@ -36,6 +36,7 @@ import { pollOptionCreator } from './poll'; import { warnAboutPMs } from './pmwarning'; import { imageSourcesCreator } from './sources'; import { sizeGraphs } from './graph'; +import { setupSliders } from './slider'; whenReady(() => { @@ -69,6 +70,7 @@ whenReady(() => { pollOptionCreator(); warnAboutPMs(); imageSourcesCreator(); + setupSliders(); sizeGraphs(); }); diff --git a/lib/philomena_web/templates/profile/_statistics.html.slime b/lib/philomena_web/templates/profile/_statistics.html.slime index a5cbdbb0f..c5bde5987 100644 --- a/lib/philomena_web/templates/profile/_statistics.html.slime +++ b/lib/philomena_web/templates/profile/_statistics.html.slime @@ -3,26 +3,50 @@ .block__content .statistics .statistics__statistic - .statistics__column Uploads - .statistics__column = number_with_delimiter(@user.uploads_count) - .statistics__column: .sparkline = sparkline_data(@statistics.uploads) + h5 + | Uploads ( + = number_with_delimiter(@user.uploads_count) + | ) + .sparkline#js-upload-graph = sparkline_data(@statistics.uploads) + .slider__minmax + input#js-graph-slider type="dualrange" min="0" max="100" valuemin="0" valuemax="100" data-target="#js-upload-graph" .statistics__statistic - .statistics__column Favorites - .statistics__column = number_with_delimiter(@user.images_favourited_count) - .statistics__column: .sparkline = sparkline_data(@statistics.images_favourited) + h5 + | Favorites ( + = number_with_delimiter(@user.images_favourited_count) + | ) + .sparkline#js-favorite-graph = sparkline_data(@statistics.images_favourited) + .slider__minmax + input#js-graph-slider type="dualrange" min="0" max="100" valuemin="0" valuemax="100" data-target="#js-favorite-graph" .statistics__statistic - .statistics__column Comments - .statistics__column = number_with_delimiter(@user.comments_posted_count) - .statistics__column: .sparkline = sparkline_data(@statistics.comments_posted) + h5 + | Comments ( + = number_with_delimiter(@user.comments_posted_count) + | ) + .sparkline#js-comment-graph = sparkline_data(@statistics.comments_posted) + .slider__minmax + input#js-graph-slider type="dualrange" min="0" max="100" valuemin="0" valuemax="100" data-target="#js-comment-graph" .statistics__statistic - .statistics__column Votes - .statistics__column = number_with_delimiter(@user.votes_cast_count) - .statistics__column: .sparkline = sparkline_data(@statistics.votes_cast) + h5 + | Votes ( + = number_with_delimiter(@user.votes_cast_count) + | ) + .sparkline#js-vote-graph = sparkline_data(@statistics.votes_cast) + .slider__minmax + input#js-graph-slider type="dualrange" min="0" max="100" valuemin="0" valuemax="100" data-target="#js-vote-graph" .statistics__statistic - .statistics__column Metadata Updates - .statistics__column = number_with_delimiter(@user.metadata_updates_count) - .statistics__column: .sparkline = sparkline_data(@statistics.metadata_updates) + h5 + | Metadata Updates ( + = number_with_delimiter(@user.metadata_updates_count) + | ) + .sparkline#js-metadata-graph = sparkline_data(@statistics.metadata_updates) + .slider__minmax + input#js-graph-slider type="dualrange" min="0" max="100" valuemin="0" valuemax="100" data-target="#js-metadata-graph" .statistics__statistic - .statistics__column Forum Posts - .statistics__column = number_with_delimiter(@user.forum_posts_count) - .statistics__column: .sparkline = sparkline_data(@statistics.forum_posts) + h5 + | Forum Posts ( + = number_with_delimiter(@user.forum_posts_count) + | ) + .sparkline#js-post-graph = sparkline_data(@statistics.forum_posts) + .slider__minmax + input#js-graph-slider type="dualrange" min="0" max="100" valuemin="0" valuemax="100" data-target="#js-post-graph" diff --git a/lib/philomena_web/views/profile_view.ex b/lib/philomena_web/views/profile_view.ex index 8fc93f7bc..e1e73f4de 100644 --- a/lib/philomena_web/views/profile_view.ex +++ b/lib/philomena_web/views/profile_view.ex @@ -54,7 +54,7 @@ defmodule PhilomenaWeb.ProfileView do sy = height / 20 factor = 100 / 90 - content_tag :svg, id: "js-sparkline-svg", width: "100%", preserveAspectRatio: "xMinYMin", viewBox: "0 0 #{width} #{height}" do + content_tag :svg, id: "js-graph-svg", width: "100%", preserveAspectRatio: "xMinYMin", viewBox: "0 0 #{width} #{height}" do first = List.first(data) last = List.last(data) first_y = sparkline_y(first, max) * sy @@ -80,7 +80,7 @@ defmodule PhilomenaWeb.ProfileView do end end - graph = content_tag :path, "", id: "js-barline-graph", class: "barline__bar", d: "M0,#{first_y}#{points}L#{width - sx},#{last_y}L#{width - sx},#{height}L0,#{height}L0,#{first_y}" + graph = content_tag :path, "", id: "js-graph", class: "barline__bar", d: "M0,#{first_y}#{points}L#{width - sx},#{last_y}L#{width - sx},#{height}L0,#{height}L0,#{first_y}" [graph, circles] end From 0877ac685ba4fdbdeff5024cb9fb2873ce44d2df Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Mon, 3 Jun 2024 23:43:51 +0200 Subject: [PATCH 29/65] document and optimize --- assets/js/slider.ts | 88 +++++++++++++++----------- assets/js/utils/__tests__/lerp.spec.ts | 17 +++++ assets/js/utils/lerp.ts | 12 ++++ 3 files changed, 79 insertions(+), 38 deletions(-) create mode 100644 assets/js/utils/__tests__/lerp.spec.ts create mode 100644 assets/js/utils/lerp.ts diff --git a/assets/js/slider.ts b/assets/js/slider.ts index fee08a515..923044884 100644 --- a/assets/js/slider.ts +++ b/assets/js/slider.ts @@ -8,15 +8,10 @@ * */ +import { lerp } from './utils/lerp'; import { $$ } from './utils/dom'; -function lerp(delta: number, from: number, to: number): number { - if (delta >= 1) { return to; } - else if (delta <= 0) { return from; } - - return from + (to - from) * delta; -} - +// Make a given slider head draggable. function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: string, limitProperty: string) { const parent = el.parentElement; @@ -24,44 +19,56 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: return; } + // Initialize variables and constants. + const inputEvent = new InputEvent('input'); let minPos = 0; let maxPos = 0; + let cachedMin = 0; + let cachedMax = 0; + let cachedValue = 0; + let cachedLimit = 0; let curValue = 0; let dragging = false; - function initVars() { - if (!parent) { return; } - - const rect = parent.getBoundingClientRect(); - - minPos = rect.x; - maxPos = rect.x + rect.width - el.clientWidth; - curValue = Number(dataEl.getAttribute(valueProperty) || '0'); - } - + // Clamps the slider head value to not cross over the other slider head's value. function clampValue(value: number): number { - const storedValue = Number(dataEl.getAttribute(valueProperty) || '0'); - const limitValue = Number(dataEl.getAttribute(limitProperty) || '0'); - - if (storedValue >= limitValue && value < limitValue) { - return limitValue; + if (cachedValue >= cachedLimit && value < cachedLimit) { + return cachedLimit; } - else if (storedValue < limitValue && value >= limitValue) { - return limitValue - 1; // Offset by 1 to ensure stored value is less than limit. + else if (cachedValue < cachedLimit && value >= cachedLimit) { + return cachedLimit - 1; // Offset by 1 to ensure stored value is less than limit. } return value; } + // Utility accessor to get the minimum value of dualrange. function getMin(): number { return Number(dataEl.getAttribute('min') || '0'); } + // Utility accessor to get the maximum value of dualrange. function getMax(): number { return Number(dataEl.getAttribute('max') || '0'); } - // Define functions to control the drag behavior of the slider. + // Initializes cached variables. Should be used + // when the pointer event begins. + function initVars() { + if (!parent) { return; } + + const rect = parent.getBoundingClientRect(); + + minPos = rect.x; + maxPos = rect.x + rect.width - el.clientWidth; + cachedMin = getMin(); + cachedMax = getMax(); + cachedValue = Number(dataEl.getAttribute(valueProperty) || '0'); + cachedLimit = Number(dataEl.getAttribute(limitProperty) || '0'); + curValue = Number(dataEl.getAttribute(valueProperty) || '0'); + } + + // Called during pointer movement. function dragMove(e: PointerEvent) { if (!dragging) { return; } @@ -69,40 +76,40 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: let desiredPos = e.clientX; - if (desiredPos > maxPos) { - desiredPos = maxPos; - } - else if (desiredPos < minPos) { - desiredPos = minPos; - } - + // `lerp` cleverly clamps the value between min and max, + // so no need for any explicit checks for that here, only + // the crossover check is required. curValue = clampValue( lerp( (desiredPos - minPos) / (maxPos - minPos), - getMin(), - getMax() + cachedMin, + cachedMax ) ); - desiredPos = lerp(curValue / getMax(), minPos, maxPos); + // Same here, lerp clamps the value so it doesn't get out + // of the slider boundary. + desiredPos = lerp(curValue / cachedMax, minPos, maxPos); el.style.left = `${desiredPos}px`; dataEl.setAttribute(valueProperty, curValue.toString()); - dataEl.dispatchEvent(new InputEvent('input')); + dataEl.dispatchEvent(inputEvent); } + // Called when the pointer is let go of. function dragEnd(e: PointerEvent) { if (!dragging) { return; } e.preventDefault(); dataEl.setAttribute(valueProperty, curValue.toString()); - dataEl.dispatchEvent(new InputEvent('input')); + dataEl.dispatchEvent(inputEvent); dragging = false; } + // Called when the slider head is clicked or tapped. function dragBegin(e: PointerEvent) { if (!parent) { return; } @@ -112,7 +119,7 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: dragging = true; } - // Set initial position; + // Set the initial variables and position; initVars(); el.style.left = `${lerp(curValue / getMax(), minPos, maxPos)}px`; @@ -122,6 +129,10 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: window.addEventListener('pointermove', dragMove); } +// Sets up the slider input element. +// Creates `div` elements for presentation, hides the +// original `input` element. The logic is that +// we use divs for presentation, and input for data storage. function setupSlider(el: HTMLInputElement) { const parent = el.parentElement; @@ -155,6 +166,7 @@ function setupSlider(el: HTMLInputElement) { setupDrag(maxHead, el, 'valuemax', 'valuemin'); } +// Sets up all sliders currently on the page. function setupSliders() { $$('input[type="dualrange"]').forEach(el => { setupSlider(el); diff --git a/assets/js/utils/__tests__/lerp.spec.ts b/assets/js/utils/__tests__/lerp.spec.ts new file mode 100644 index 000000000..189f37e70 --- /dev/null +++ b/assets/js/utils/__tests__/lerp.spec.ts @@ -0,0 +1,17 @@ +import { lerp } from '../lerp'; + +describe('Linear interpolation', () => { + describe('lerp', () => { + it('should interpolate the min-max range based on a delta', () => { + expect(lerp(0.5, 0, 100)).toEqual(50); + expect(lerp(0.75, 0, 100)).toEqual(75); + }); + + it('should clamp the value between min and max', () => { + expect(lerp(-999, 0, 100)).toEqual(0); + expect(lerp(0, 0, 100)).toEqual(0); + expect(lerp(999, 0, 100)).toEqual(100); + expect(lerp(1, 0, 100)).toEqual(100); + }); + }); +}); diff --git a/assets/js/utils/lerp.ts b/assets/js/utils/lerp.ts new file mode 100644 index 000000000..41170284f --- /dev/null +++ b/assets/js/utils/lerp.ts @@ -0,0 +1,12 @@ +// Simple linear interpolation. +// Returns a value between min and max based on a delta. +// The delta is a number between 0 and 1. +// If the delta is not within the 0-1 range, this function will +// clamp the value between min and max, depending on whether +// the delta >= 1 or <= 0. +export function lerp(delta: number, from: number, to: number): number { + if (delta >= 1) { return to; } + else if (delta <= 0) { return from; } + + return from + (to - from) * delta; +} From 97577db06585d9ee078607ef1ec2388a6399ce2c Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Tue, 4 Jun 2024 21:13:31 +0200 Subject: [PATCH 30/65] port staffhider to ts, eslint --fix, new fp --- assets/js/fp.js | 109 ++++++++++++++++----- assets/js/{staffhider.js => staffhider.ts} | 6 +- assets/js/utils/__tests__/lerp.spec.ts | 2 +- assets/types/booru-object.d.ts | 4 + 4 files changed, 92 insertions(+), 29 deletions(-) rename assets/js/{staffhider.js => staffhider.ts} (67%) diff --git a/assets/js/fp.js b/assets/js/fp.js index e84e5b98d..9ee3a03df 100644 --- a/assets/js/fp.js +++ b/assets/js/fp.js @@ -1,48 +1,109 @@ /** - * Thanks uBlock for breaking our JS! + * FP version 4 + * + * Not reliant on deprecated properties, + * and potentially more accurate at what it's supposed to do. */ +import { $ } from './utils/dom'; +import store from './utils/store'; + // http://stackoverflow.com/a/34842797 function hashCode(str) { return str.split('').reduce((prevHash, currVal) => ((prevHash << 5) - prevHash) + currVal.charCodeAt(0), 0) >>> 0; } -function createFp() { +async function createFp() { + let kb = 'none'; + let mem = '1'; + let ua = 'none'; + + if (navigator.keyboard) { + kb = (await navigator.keyboard.getLayoutMap()).entries().toArray().sort().map(e => `${e[0]}${e[1]}`).join(''); + } + + if (navigator.deviceMemory) { + mem = navigator.deviceMemory.toString(); + } + + if (navigator.userAgentData) { + const uadata = navigator.userAgentData; + let brands = 'none'; + + if (uadata.brands && uadata.brands.length > 0) { + brands = uadata.brands.filter(e => !e.brand.match(/.*ot.*rand.*/gi)).map(e => `${e.brand}${e.version}`).join(''); + } + + ua = `${brands}${uadata.mobile}${uadata.platform}`; + } + + let width = store.get('cached_rem_size'); + const body = $('body'); + + if (!width && body) { + const testElement = document.createElement('span'); + testElement.style.minWidth = '1rem'; + testElement.style.maxWidth = '1rem'; + testElement.style.position = 'absolute'; + + body.appendChild(testElement); + + width = testElement.clientWidth.toString(); + + body.removeChild(testElement); + + store.set('cached_rem_size', width); + } + + if (!width) { + width = '0'; + } + const prints = [ navigator.userAgent, - navigator.cpuClass, - navigator.oscpu, - navigator.platform, - - navigator.browserLanguage, + navigator.hardwareConcurrency.toString(), + navigator.maxTouchPoints.toString(), navigator.language, - navigator.systemLanguage, - navigator.userLanguage, + kb, + mem, + ua, + width, - screen.availLeft, - screen.availTop, - screen.availWidth, - screen.height, - screen.width, + screen.height.toString(), + screen.width.toString(), + screen.colorDepth.toString(), + screen.pixelDepth.toString(), - window.devicePixelRatio, - new Date().getTimezoneOffset(), + window.devicePixelRatio.toString(), + new Date().getTimezoneOffset().toString(), ]; return hashCode(prints.join('')); } -function setFpCookie() { - let fp; +async function setFpCookie() { + let fp = store.get('cached_ses_value'); + + if (!fp) { + const m = document.cookie.match(/_ses=([a-f\d]+)/); - // The prepended 'c' acts as a crude versioning mechanism. - try { - fp = `c${createFp()}`; + if (m && m[1]) { + fp = m[1]; + } } - // If it fails, use fakeprint "c1836832948" as a last resort. - catch (err) { - fp = 'c1836832948'; + + if (!fp || fp.charAt(0) !== 'd') { + // The prepended 'd' acts as a crude versioning mechanism. + try { + fp = `d${await createFp()}`; + } + // If it fails, use fakeprint "d1836832948" as a last resort. + catch (err) { + fp = 'd1836832948'; + } + + store.set('cached_ses_value', fp); } document.cookie = `_ses=${fp}; path=/; SameSite=Lax`; diff --git a/assets/js/staffhider.js b/assets/js/staffhider.ts similarity index 67% rename from assets/js/staffhider.js rename to assets/js/staffhider.ts index 68b64447f..75f4ed132 100644 --- a/assets/js/staffhider.js +++ b/assets/js/staffhider.ts @@ -6,12 +6,10 @@ import { $$ } from './utils/dom'; -function hideStaffTools() { +export function hideStaffTools() { if (window.booru.hideStaffTools === 'true') { - $$('.js-staff-action').forEach(el => { + $$('.js-staff-action').forEach(el => { el.classList.add('hidden'); }); } } - -export { hideStaffTools }; diff --git a/assets/js/utils/__tests__/lerp.spec.ts b/assets/js/utils/__tests__/lerp.spec.ts index 189f37e70..3c29ab2ae 100644 --- a/assets/js/utils/__tests__/lerp.spec.ts +++ b/assets/js/utils/__tests__/lerp.spec.ts @@ -6,7 +6,7 @@ describe('Linear interpolation', () => { expect(lerp(0.5, 0, 100)).toEqual(50); expect(lerp(0.75, 0, 100)).toEqual(75); }); - + it('should clamp the value between min and max', () => { expect(lerp(-999, 0, 100)).toEqual(0); expect(lerp(0, 0, 100)).toEqual(0); diff --git a/assets/types/booru-object.d.ts b/assets/types/booru-object.d.ts index 22d1aa08a..2a5949c03 100644 --- a/assets/types/booru-object.d.ts +++ b/assets/types/booru-object.d.ts @@ -65,6 +65,10 @@ interface BooruObject { spoileredFilter: AstMatcher; tagsVersion: number; interactions: Interaction[]; + /** + * Indicates whether sensitive staff-only info should be hidden or not. + */ + hideStaffTools: string; } declare global { From b6973eb4370b3f44a502f857c6293e24ccb1bcd2 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 5 Jun 2024 22:56:48 +0200 Subject: [PATCH 31/65] port stuff to ts --- assets/js/fp.js | 112 ------------------------- assets/js/fp.ts | 163 ++++++++++++++++++++++++++++++++++++ assets/js/when-ready.js | 4 +- assets/test/vitest-setup.ts | 3 +- 4 files changed, 167 insertions(+), 115 deletions(-) delete mode 100644 assets/js/fp.js create mode 100644 assets/js/fp.ts diff --git a/assets/js/fp.js b/assets/js/fp.js deleted file mode 100644 index 9ee3a03df..000000000 --- a/assets/js/fp.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * FP version 4 - * - * Not reliant on deprecated properties, - * and potentially more accurate at what it's supposed to do. - */ - -import { $ } from './utils/dom'; -import store from './utils/store'; - -// http://stackoverflow.com/a/34842797 -function hashCode(str) { - return str.split('').reduce((prevHash, currVal) => - ((prevHash << 5) - prevHash) + currVal.charCodeAt(0), 0) >>> 0; -} - -async function createFp() { - let kb = 'none'; - let mem = '1'; - let ua = 'none'; - - if (navigator.keyboard) { - kb = (await navigator.keyboard.getLayoutMap()).entries().toArray().sort().map(e => `${e[0]}${e[1]}`).join(''); - } - - if (navigator.deviceMemory) { - mem = navigator.deviceMemory.toString(); - } - - if (navigator.userAgentData) { - const uadata = navigator.userAgentData; - let brands = 'none'; - - if (uadata.brands && uadata.brands.length > 0) { - brands = uadata.brands.filter(e => !e.brand.match(/.*ot.*rand.*/gi)).map(e => `${e.brand}${e.version}`).join(''); - } - - ua = `${brands}${uadata.mobile}${uadata.platform}`; - } - - let width = store.get('cached_rem_size'); - const body = $('body'); - - if (!width && body) { - const testElement = document.createElement('span'); - testElement.style.minWidth = '1rem'; - testElement.style.maxWidth = '1rem'; - testElement.style.position = 'absolute'; - - body.appendChild(testElement); - - width = testElement.clientWidth.toString(); - - body.removeChild(testElement); - - store.set('cached_rem_size', width); - } - - if (!width) { - width = '0'; - } - - const prints = [ - navigator.userAgent, - navigator.hardwareConcurrency.toString(), - navigator.maxTouchPoints.toString(), - navigator.language, - kb, - mem, - ua, - width, - - screen.height.toString(), - screen.width.toString(), - screen.colorDepth.toString(), - screen.pixelDepth.toString(), - - window.devicePixelRatio.toString(), - new Date().getTimezoneOffset().toString(), - ]; - - return hashCode(prints.join('')); -} - -async function setFpCookie() { - let fp = store.get('cached_ses_value'); - - if (!fp) { - const m = document.cookie.match(/_ses=([a-f\d]+)/); - - if (m && m[1]) { - fp = m[1]; - } - } - - if (!fp || fp.charAt(0) !== 'd') { - // The prepended 'd' acts as a crude versioning mechanism. - try { - fp = `d${await createFp()}`; - } - // If it fails, use fakeprint "d1836832948" as a last resort. - catch (err) { - fp = 'd1836832948'; - } - - store.set('cached_ses_value', fp); - } - - document.cookie = `_ses=${fp}; path=/; SameSite=Lax`; -} - -export { setFpCookie }; diff --git a/assets/js/fp.ts b/assets/js/fp.ts new file mode 100644 index 000000000..84a1557bc --- /dev/null +++ b/assets/js/fp.ts @@ -0,0 +1,163 @@ +/** + * FP version 4 + * + * Not reliant on deprecated properties, + * and potentially more accurate at what it's supposed to do. + */ + +import { $ } from './utils/dom'; +import store from './utils/store'; + +interface RealKeyboard { + getLayoutMap: () => Promise> +} + +interface RealUserAgentData { + brands: [{brand: string, version: string}], + mobile: boolean, + platform: string, +} + +interface RealNavigator extends Navigator { + deviceMemory: number, + keyboard: RealKeyboard, + userAgentData: RealUserAgentData, +} + +/** + * Creates a 53-bit long hash of a string. + * + * @param {string} str The string to hash. + * @param {number} seed The seed to use for hash generation. + * @return {number} The resulting hash as a 53-bit number. + * @see {@link https://stackoverflow.com/a/8831937} + */ +function cyrb53(str: string, seed: number = 0x16fe7b0a): number { + let h1 = 0xdeadbeef ^ seed; + let h2 = 0x41c6ce57 ^ seed; + + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); + } + + h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507); + h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909); + h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507); + h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909); + + return 4294967296 * (2097151 & h2) + (h1 >>> 0); +} + +/** Creates a semi-unique string from browser attributes. + * + * @async + * @return {Promise} Hexadecimally encoded 53 bit number padded to 7 bytes. + */ +async function createFp(): Promise { + const nav = navigator as RealNavigator; + let kb = 'none'; + let mem = '1'; + let ua = 'none'; + + if (nav.keyboard) { + kb = Array.from((await nav.keyboard.getLayoutMap()).entries()).sort().map(e => `${e[0]}${e[1]}`).join(''); + } + + if (nav.deviceMemory) { + mem = nav.deviceMemory.toString(); + } + + if (nav.userAgentData) { + const uadata = nav.userAgentData; + let brands = 'none'; + + if (uadata.brands && uadata.brands.length > 0) { + brands = uadata.brands.filter(e => !e.brand.match(/.*ot.*rand.*/gi)).map(e => `${e.brand}${e.version}`).join(''); + } + + ua = `${brands}${uadata.mobile}${uadata.platform}`; + } + + let width: string | null = store.get('cached_rem_size'); + const body = $('body'); + + if (!width && body) { + const testElement = document.createElement('span'); + testElement.style.minWidth = '1rem'; + testElement.style.maxWidth = '1rem'; + testElement.style.position = 'absolute'; + + body.appendChild(testElement); + + width = testElement.clientWidth.toString(); + + body.removeChild(testElement); + + store.set('cached_rem_size', width); + } + + if (!width) { + width = '0'; + } + + const prints: string[] = [ + navigator.userAgent, + navigator.hardwareConcurrency.toString(), + navigator.maxTouchPoints.toString(), + navigator.language, + kb, + mem, + ua, + width, + + screen.height.toString(), + screen.width.toString(), + screen.colorDepth.toString(), + screen.pixelDepth.toString(), + + window.devicePixelRatio.toString(), + new Date().getTimezoneOffset().toString(), + ]; + + return cyrb53(prints.join('')).toString(16).padStart(14, '0'); +} + +/** + * Sets the `_ses` cookie. + * + * If `cached_ses_value` is present in local storage, uses that instead. + * Otherwise if the `_ses` cookie already exits, uses its value instead. + * Otherwise attempts to generate a new value for the `_ses` cookie + * based on various browser attributes. + * Failing all previous methods, sets the `_ses` cookie to a fallback value. + * + * @async + */ +export async function setSesCookie() { + let fp: string | null = store.get('cached_ses_value'); + + if (!fp) { + const m = document.cookie.match(/_ses=([a-f0-9]+)/); + + if (m && m[1]) { + fp = m[1]; + } + } + + if (!fp || fp.charAt(0) !== 'd' || fp.length !== 15) { + // The prepended 'd' acts as a crude versioning mechanism. + try { + fp = `d${await createFp()}`; + } + // If it fails, use fakeprint "d015c342859dde3" as a last resort. + catch (err) { + fp = 'd015c342859dde3'; + } + + store.set('cached_ses_value', fp); + } + + document.cookie = `_ses=${fp}; path=/; SameSite=Lax`; +} diff --git a/assets/js/when-ready.js b/assets/js/when-ready.js index 2eb90aa6c..f39cec694 100644 --- a/assets/js/when-ready.js +++ b/assets/js/when-ready.js @@ -14,7 +14,7 @@ import { setupBurgerMenu } from './burger'; import { bindCaptchaLinks } from './captcha'; import { setupComments } from './comment'; import { setupDupeReports } from './duplicate_reports'; -import { setFpCookie } from './fp'; +import { setSesCookie } from './fp'; import { setupGalleryEditing } from './galleries'; import { initImagesClientside } from './imagesclientside'; import { bindImageTarget } from './image_expansion'; @@ -50,7 +50,7 @@ whenReady(() => { initImagesClientside(); setupComments(); setupDupeReports(); - setFpCookie(); + setSesCookie(); setupGalleryEditing(); bindImageTarget(); setupEvents(); diff --git a/assets/test/vitest-setup.ts b/assets/test/vitest-setup.ts index d889b27f4..e4cd1314d 100644 --- a/assets/test/vitest-setup.ts +++ b/assets/test/vitest-setup.ts @@ -20,7 +20,8 @@ window.booru = { hiddenFilter: matchNone(), spoileredFilter: matchNone(), interactions: [], - tagsVersion: 5 + tagsVersion: 5, + hideStaffTools: 'false' }; // https://github.com/jsdom/jsdom/issues/1721#issuecomment-1484202038 From 8ca39020059e6e195eea4e7e99d56e4fddf2bb63 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Thu, 6 Jun 2024 18:48:16 +0200 Subject: [PATCH 32/65] port a bunch of stuff to typescript (untested) --- assets/js/{app.js => app.ts} | 3 +- assets/js/{captcha.js => captcha.ts} | 12 +- assets/js/comment.js | 23 +-- assets/js/communications/comment.js | 10 -- assets/js/communications/post.js | 10 -- assets/js/duplicate_reports.js | 42 ------ assets/js/duplicate_reports.ts | 45 ++++++ assets/js/fp.ts | 10 +- assets/js/{galleries.js => galleries.ts} | 24 ++-- assets/js/imagesclientside.js | 76 ---------- assets/js/imagesclientside.ts | 131 ++++++++++++++++++ assets/js/{pmwarning.js => pmwarning.ts} | 4 +- assets/js/{poll.js => poll.ts} | 4 +- assets/js/search.js | 45 ------ assets/js/search.ts | 50 +++++++ assets/js/{settings.js => settings.ts} | 14 +- assets/js/shortcuts.js | 62 --------- assets/js/shortcuts.ts | 76 ++++++++++ assets/js/{sources.js => sources.ts} | 14 +- assets/js/tagsmisc.js | 56 -------- assets/js/tagsmisc.ts | 70 ++++++++++ assets/js/utils/tag.ts | 4 +- assets/js/{when-ready.js => when-ready.ts} | 5 - assets/test/vitest-setup.ts | 3 +- assets/types/booru-object.d.ts | 4 + assets/vite.config.ts | 2 +- .../templates/layout/app.html.slime | 2 +- 27 files changed, 431 insertions(+), 370 deletions(-) rename assets/js/{app.js => app.ts} (91%) rename assets/js/{captcha.js => captcha.ts} (57%) delete mode 100644 assets/js/communications/comment.js delete mode 100644 assets/js/communications/post.js delete mode 100644 assets/js/duplicate_reports.js create mode 100644 assets/js/duplicate_reports.ts rename assets/js/{galleries.js => galleries.ts} (51%) delete mode 100644 assets/js/imagesclientside.js create mode 100644 assets/js/imagesclientside.ts rename assets/js/{pmwarning.js => pmwarning.ts} (80%) rename assets/js/{poll.js => poll.ts} (81%) delete mode 100644 assets/js/search.js create mode 100644 assets/js/search.ts rename assets/js/{settings.js => settings.ts} (50%) delete mode 100644 assets/js/shortcuts.js create mode 100644 assets/js/shortcuts.ts rename assets/js/{sources.js => sources.ts} (58%) delete mode 100644 assets/js/tagsmisc.js create mode 100644 assets/js/tagsmisc.ts rename assets/js/{when-ready.js => when-ready.ts} (93%) diff --git a/assets/js/app.js b/assets/js/app.ts similarity index 91% rename from assets/js/app.js rename to assets/js/app.ts index 4f23d6557..863f4ff24 100644 --- a/assets/js/app.js +++ b/assets/js/app.ts @@ -14,6 +14,7 @@ import './when-ready'; // Would typically be either the theme file, or any additional file // you later intend to put in the tag. -// import '../css/themes/default.scss'; +import '../css/application.css'; +import '../css/themes/dark-blue.css'; // import '../css/themes/dark.scss'; // import '../css/themes/red.scss'; diff --git a/assets/js/captcha.js b/assets/js/captcha.ts similarity index 57% rename from assets/js/captcha.js rename to assets/js/captcha.ts index ec0b4f32c..eef2f0b2f 100644 --- a/assets/js/captcha.js +++ b/assets/js/captcha.ts @@ -1,18 +1,20 @@ import { delegate, leftClick } from './utils/events'; import { clearEl, makeEl } from './utils/dom'; -function insertCaptcha(_event, target) { - const { parentNode, dataset: { sitekey } } = target; +function insertCaptcha(_event: Event, target: HTMLInputElement) { + const { parentElement, dataset: { sitekey } } = target; + + if (!parentElement) { return; } const script = makeEl('script', {src: 'https://hcaptcha.com/1/api.js', async: true, defer: true}); const frame = makeEl('div', {className: 'h-captcha'}); frame.dataset.sitekey = sitekey; - clearEl(parentNode); + clearEl(parentElement); - parentNode.insertAdjacentElement('beforeend', frame); - parentNode.insertAdjacentElement('beforeend', script); + parentElement.insertAdjacentElement('beforeend', frame); + parentElement.insertAdjacentElement('beforeend', script); } export function bindCaptchaLinks() { diff --git a/assets/js/comment.js b/assets/js/comment.js index de245fa8d..feb05b59a 100644 --- a/assets/js/comment.js +++ b/assets/js/comment.js @@ -3,24 +3,21 @@ */ import { $ } from './utils/dom'; -import { showOwnedComments } from './communications/comment'; import { filterNode } from './imagesclientside'; import { fetchHtml } from './utils/requests'; import { timeAgo } from './timeago'; function handleError(response) { - const errorMessage = '
Comment failed to load!
'; if (!response.ok) { return errorMessage; } - return response.text(); + return response.text(); } function commentPosted(response) { - const commentEditTab = $('#js-comment-form a[data-click-tab="write"]'), commentEditForm = $('#js-comment-form'), container = document.getElementById('comments'), @@ -43,11 +40,9 @@ function commentPosted(response) { window.location.reload(); window.scrollTo(0, 0); // Error message is displayed at the top of the page (flash) } - } function loadParentPost(event) { - const clickedLink = event.target, // Find the comment containing the link that was clicked fullComment = clickedLink.closest('article.block'), @@ -62,7 +57,6 @@ function loadParentPost(event) { } if (commentMatches) { - // If the regex matched, get the image and comment ID const [ , imageId, commentId ] = commentMatches; @@ -74,13 +68,10 @@ function loadParentPost(event) { }); return true; - } - } function insertParentPost(data, clickedLink, fullComment) { - // Add the 'subthread' class to the comment with the clicked link fullComment.classList.add('subthread'); @@ -99,11 +90,9 @@ function insertParentPost(data, clickedLink, fullComment) { // Filter images (if any) in the loaded comment filterNode(fullComment.previousSibling); - } function clearParentPost(clickedLink, fullComment) { - // Remove any previous siblings with the class fetched-comment while (fullComment.previousSibling && fullComment.previousSibling.classList.contains('fetched-comment')) { fullComment.previousSibling.parentNode.removeChild(fullComment.previousSibling); @@ -118,11 +107,9 @@ function clearParentPost(clickedLink, fullComment) { if (!fullComment.classList.contains('fetched-comment')) { fullComment.classList.remove('subthread'); } - } function displayComments(container, commentsHtml) { - container.innerHTML = commentsHtml; // Execute timeago on comments @@ -130,14 +117,9 @@ function displayComments(container, commentsHtml) { // Filter images in the comments filterNode(container); - - // Show options on own comments - showOwnedComments(); - } function loadComments(event) { - const container = document.getElementById('comments'), hasHref = event.target && event.target.getAttribute('href'), hasHash = window.location.hash && window.location.hash.match(/#comment_([a-f0-9]+)/), @@ -147,7 +129,6 @@ function loadComments(event) { fetchHtml(getURL) .then(handleError) .then(data => { - displayComments(container, data); // Make sure the :target CSS selector applies to the inserted content @@ -159,7 +140,6 @@ function loadComments(event) { }); return true; - } function setupComments() { @@ -175,7 +155,6 @@ function setupComments() { } else { filterNode(comments); - showOwnedComments(); } } diff --git a/assets/js/communications/comment.js b/assets/js/communications/comment.js deleted file mode 100644 index a4661c617..000000000 --- a/assets/js/communications/comment.js +++ /dev/null @@ -1,10 +0,0 @@ -import { $ } from '../utils/dom'; - -function showOwnedComments() { - const editableComments = $('.js-editable-comments'); - const editableCommentIds = editableComments && JSON.parse(editableComments.dataset.editable); - - if (editableCommentIds) editableCommentIds.forEach(id => $(`#comment_${id} .owner-options`).classList.remove('hidden')); -} - -export { showOwnedComments }; diff --git a/assets/js/communications/post.js b/assets/js/communications/post.js deleted file mode 100644 index d21722204..000000000 --- a/assets/js/communications/post.js +++ /dev/null @@ -1,10 +0,0 @@ -import { $ } from '../utils/dom'; - -function showOwnedPosts() { - const editablePost = $('.js-editable-posts'); - const editablePostIds = editablePost && JSON.parse(editablePost.dataset.editable); - - if (editablePostIds) editablePostIds.forEach(id => $(`#post_${id} .owner-options`).classList.remove('hidden')); -} - -export { showOwnedPosts }; diff --git a/assets/js/duplicate_reports.js b/assets/js/duplicate_reports.js deleted file mode 100644 index a6794ec49..000000000 --- a/assets/js/duplicate_reports.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Interactive behavior for duplicate reports. - */ - -import { $, $$ } from './utils/dom'; - -function setupDupeReports() { - const [ onion, slider ] = $$('.onion-skin__image, .onion-skin__slider'); - const swipe = $('.swipe__image'); - - if (swipe) setupSwipe(swipe); - if (onion) setupOnionSkin(onion, slider); -} - -function setupSwipe(swipe) { - const [ clip, divider ] = $$('#clip rect, #divider', swipe); - const { width } = swipe.viewBox.baseVal; - - function moveDivider({ clientX }) { - // Move center to cursor - const rect = swipe.getBoundingClientRect(); - const newX = (clientX - rect.left) * (width / rect.width); - - divider.setAttribute('x', newX); - clip.setAttribute('width', newX); - } - - swipe.addEventListener('mousemove', moveDivider); -} - -function setupOnionSkin(onion, slider) { - const target = $('#target', onion); - - function setOpacity() { - target.setAttribute('opacity', slider.value); - } - - setOpacity(); - slider.addEventListener('input', setOpacity); -} - -export { setupDupeReports }; diff --git a/assets/js/duplicate_reports.ts b/assets/js/duplicate_reports.ts new file mode 100644 index 000000000..12eb8c2c2 --- /dev/null +++ b/assets/js/duplicate_reports.ts @@ -0,0 +1,45 @@ +/** + * Interactive behavior for duplicate reports. + */ + +import { $, $$ } from './utils/dom'; + +function setupDupeReports() { + const onion = $('.onion-skin__image'); + const slider = $('.onion-skin__slider'); + const swipe = $('.swipe__image'); + + if (swipe) setupSwipe(swipe); + if (onion && slider) setupOnionSkin(onion, slider); +} + +function setupSwipe(swipe: SVGSVGElement) { + const [ clip, divider ] = $$('#clip rect, #divider', swipe); + const { width } = swipe.viewBox.baseVal; + + function moveDivider({ clientX }: MouseEvent) { + // Move center to cursor + const rect = swipe.getBoundingClientRect(); + const newX = (clientX - rect.left) * (width / rect.width); + + divider.setAttribute('x', newX.toString()); + clip.setAttribute('width', newX.toString()); + } + + swipe.addEventListener('mousemove', moveDivider); +} + +function setupOnionSkin(onion: SVGSVGElement, slider: HTMLInputElement) { + const target = $('#target', onion); + + function setOpacity() { + if (target) { + target.setAttribute('opacity', slider.value); + } + } + + setOpacity(); + slider.addEventListener('input', setOpacity); +} + +export { setupDupeReports }; diff --git a/assets/js/fp.ts b/assets/js/fp.ts index 84a1557bc..49182bc90 100644 --- a/assets/js/fp.ts +++ b/assets/js/fp.ts @@ -19,9 +19,9 @@ interface RealUserAgentData { } interface RealNavigator extends Navigator { - deviceMemory: number, - keyboard: RealKeyboard, - userAgentData: RealUserAgentData, + deviceMemory: number | null, + keyboard: RealKeyboard | null, + userAgentData: RealUserAgentData | null, } /** @@ -81,7 +81,7 @@ async function createFp(): Promise { } let width: string | null = store.get('cached_rem_size'); - const body = $('body'); + const body = $('body'); if (!width && body) { const testElement = document.createElement('span'); @@ -127,7 +127,7 @@ async function createFp(): Promise { /** * Sets the `_ses` cookie. * - * If `cached_ses_value` is present in local storage, uses that instead. + * If `cached_ses_value` is present in local storage, uses it to set the `_ses` cookie. * Otherwise if the `_ses` cookie already exits, uses its value instead. * Otherwise attempts to generate a new value for the `_ses` cookie * based on various browser attributes. diff --git a/assets/js/galleries.js b/assets/js/galleries.ts similarity index 51% rename from assets/js/galleries.js rename to assets/js/galleries.ts index e881fa659..7866de74d 100644 --- a/assets/js/galleries.js +++ b/assets/js/galleries.ts @@ -8,11 +8,13 @@ import { initDraggables } from './utils/draggable'; import { fetchJson } from './utils/requests'; export function setupGalleryEditing() { - if (!$('.rearrange-button')) return; + if (!$('.rearrange-button')) return; - const [ rearrangeEl, saveEl ] = $$('.rearrange-button'); - const sortableEl = $('#sortable'); - const containerEl = $('.media-list'); + const [ rearrangeEl, saveEl ] = $$('.rearrange-button'); + const sortableEl = $('#sortable'); + const containerEl = $('.media-list'); + + if (!sortableEl || !containerEl || !saveEl || !rearrangeEl) { return; } // Copy array let oldImages = window.booru.galleryImages.slice(); @@ -20,7 +22,7 @@ export function setupGalleryEditing() { initDraggables(); - $$('.media-box', containerEl).forEach(i => i.draggable = true); + $$('.media-box', containerEl).forEach(i => i.draggable = true); rearrangeEl.addEventListener('click', () => { sortableEl.classList.add('editing'); @@ -31,15 +33,17 @@ export function setupGalleryEditing() { sortableEl.classList.remove('editing'); containerEl.classList.remove('drag-container'); - newImages = $$('.image-container', containerEl).map(i => parseInt(i.dataset.imageId, 10)); + newImages = $$('.image-container', containerEl).map(i => parseInt(i.dataset.imageId || '-1', 10)); // If nothing changed, don't bother. if (arraysEqual(newImages, oldImages)) return; - fetchJson('PATCH', saveEl.dataset.reorderPath, { - image_ids: newImages, + if (saveEl.dataset.reorderPath) { + fetchJson('PATCH', saveEl.dataset.reorderPath, { + image_ids: newImages, - // copy the array again so that we have the newly updated set - }).then(() => oldImages = newImages.slice()); + // copy the array again so that we have the newly updated set + }).then(() => oldImages = newImages.slice()); + } }); } diff --git a/assets/js/imagesclientside.js b/assets/js/imagesclientside.js deleted file mode 100644 index 7dc3fb31a..000000000 --- a/assets/js/imagesclientside.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Client-side image filtering/spoilering. - */ - -import { $$, escapeHtml } from './utils/dom'; -import { setupInteractions } from './interactions'; -import { showThumb, showBlock, spoilerThumb, spoilerBlock, hideThumb } from './utils/image'; -import { getHiddenTags, getSpoileredTags, imageHitsTags, imageHitsComplex, displayTags } from './utils/tag'; - -function runFilter(img, test, runCallback) { - if (!test || test.length === 0) return false; - - runCallback(img, test); - - // I don't like this. - window.booru.imagesWithDownvotingDisabled.push(img.dataset.imageId); - - return true; -} - -// --- - -function filterThumbSimple(img, tagsHit) { hideThumb(img, tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, `[HIDDEN] ${displayTags(tagsHit)}`); } -function spoilerThumbSimple(img, tagsHit) { spoilerThumb(img, tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, displayTags(tagsHit)); } -function filterThumbComplex(img) { hideThumb(img, window.booru.hiddenTag, '[HIDDEN] (Complex Filter)'); } -function spoilerThumbComplex(img) { spoilerThumb(img, window.booru.hiddenTag, '(Complex Filter)'); } - -function filterBlockSimple(img, tagsHit) { spoilerBlock(img, tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is hidden by `); } -function spoilerBlockSimple(img, tagsHit) { spoilerBlock(img, tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is spoilered by `); } -function filterBlockComplex(img) { spoilerBlock(img, window.booru.hiddenTag, 'This image was hidden by a complex tag expression in '); } -function spoilerBlockComplex(img) { spoilerBlock(img, window.booru.hiddenTag, 'This image was spoilered by a complex tag expression in '); } - -// --- - -function thumbTagFilter(tags, img) { return runFilter(img, imageHitsTags(img, tags), filterThumbSimple); } -function thumbComplexFilter(complex, img) { return runFilter(img, imageHitsComplex(img, complex), filterThumbComplex); } -function thumbTagSpoiler(tags, img) { return runFilter(img, imageHitsTags(img, tags), spoilerThumbSimple); } -function thumbComplexSpoiler(complex, img) { return runFilter(img, imageHitsComplex(img, complex), spoilerThumbComplex); } - -function blockTagFilter(tags, img) { return runFilter(img, imageHitsTags(img, tags), filterBlockSimple); } -function blockComplexFilter(complex, img) { return runFilter(img, imageHitsComplex(img, complex), filterBlockComplex); } -function blockTagSpoiler(tags, img) { return runFilter(img, imageHitsTags(img, tags), spoilerBlockSimple); } -function blockComplexSpoiler(complex, img) { return runFilter(img, imageHitsComplex(img, complex), spoilerBlockComplex); } - -// --- - -function filterNode(node = document) { - const hiddenTags = getHiddenTags(), spoileredTags = getSpoileredTags(); - const { hiddenFilter, spoileredFilter } = window.booru; - - // Image thumb boxes with vote and fave buttons on them - $$('.image-container', node) - .filter(img => !thumbTagFilter(hiddenTags, img)) - .filter(img => !thumbComplexFilter(hiddenFilter, img)) - .filter(img => !thumbTagSpoiler(spoileredTags, img)) - .filter(img => !thumbComplexSpoiler(spoileredFilter, img)) - .forEach(img => showThumb(img)); - - // Individual image pages and images in posts/comments - $$('.image-show-container', node) - .filter(img => !blockTagFilter(hiddenTags, img)) - .filter(img => !blockComplexFilter(hiddenFilter, img)) - .filter(img => !blockTagSpoiler(spoileredTags, img)) - .filter(img => !blockComplexSpoiler(spoileredFilter, img)) - .forEach(img => showBlock(img)); -} - -function initImagesClientside() { - window.booru.imagesWithDownvotingDisabled = []; - // This fills the imagesWithDownvotingDisabled array - filterNode(document); - // Once the array is populated, we can initialize interactions - setupInteractions(); -} - -export { initImagesClientside, filterNode }; diff --git a/assets/js/imagesclientside.ts b/assets/js/imagesclientside.ts new file mode 100644 index 000000000..47fef0e9b --- /dev/null +++ b/assets/js/imagesclientside.ts @@ -0,0 +1,131 @@ +/** + * Client-side image filtering/spoilering. + */ + +import { $$, escapeHtml } from './utils/dom'; +import { setupInteractions } from './interactions'; +import { showThumb, showBlock, spoilerThumb, spoilerBlock, hideThumb } from './utils/image'; +import { TagData, getHiddenTags, getSpoileredTags, imageHitsTags, imageHitsComplex, displayTags } from './utils/tag'; +import { AstMatcher } from './query/types'; + +type RunFilterCallback = (img: HTMLDivElement, test: TagData[]) => void; + +function runFilter(img: HTMLDivElement, test: TagData[] | boolean, runCallback: RunFilterCallback) { + if (!test || typeof test !== 'boolean' && test.length === 0) { return false; } + + runCallback(img, test as TagData[]); + + // I don't like this. + img.dataset.imageId && window.booru.imagesWithDownvotingDisabled.push(img.dataset.imageId); + + return true; +} + +// --- + +function filterThumbSimple(img: HTMLDivElement, tagsHit: TagData[]) { + hideThumb(img, tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, `[HIDDEN] ${displayTags(tagsHit)}`); +} + +function spoilerThumbSimple(img: HTMLDivElement, tagsHit: TagData[]) { + spoilerThumb(img, tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, displayTags(tagsHit)); +} + +function filterThumbComplex(img: HTMLDivElement) { + hideThumb(img, window.booru.hiddenTag, '[HIDDEN] (Complex Filter)'); +} + +function spoilerThumbComplex(img: HTMLDivElement) { + spoilerThumb(img, window.booru.hiddenTag, '(Complex Filter)'); +} + +function filterBlockSimple(img: HTMLDivElement, tagsHit: TagData[]) { + spoilerBlock( + img, + tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, + `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is hidden by ` + ); +} + +function spoilerBlockSimple(img: HTMLDivElement, tagsHit: TagData[]) { + spoilerBlock( + img, + tagsHit[0].spoiler_image_uri || window.booru.hiddenTag, + `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is spoilered by ` + ); +} + +function filterBlockComplex(img: HTMLDivElement) { + spoilerBlock(img, window.booru.hiddenTag, 'This image was hidden by a complex tag expression in '); +} + +function spoilerBlockComplex(img: HTMLDivElement) { + spoilerBlock(img, window.booru.hiddenTag, 'This image was spoilered by a complex tag expression in '); +} + +// --- + +function thumbTagFilter(tags: TagData[], img: HTMLDivElement) { + return runFilter(img, imageHitsTags(img, tags), filterThumbSimple); +} + +function thumbComplexFilter(complex: AstMatcher, img: HTMLDivElement) { + return runFilter(img, imageHitsComplex(img, complex), filterThumbComplex); +} + +function thumbTagSpoiler(tags: TagData[], img: HTMLDivElement) { + return runFilter(img, imageHitsTags(img, tags), spoilerThumbSimple); +} + +function thumbComplexSpoiler(complex: AstMatcher, img: HTMLDivElement) { + return runFilter(img, imageHitsComplex(img, complex), spoilerThumbComplex); +} + +function blockTagFilter(tags: TagData[], img: HTMLDivElement) { + return runFilter(img, imageHitsTags(img, tags), filterBlockSimple); +} + +function blockComplexFilter(complex: AstMatcher, img: HTMLDivElement) { + return runFilter(img, imageHitsComplex(img, complex), filterBlockComplex); +} + +function blockTagSpoiler(tags: TagData[], img: HTMLDivElement) { + return runFilter(img, imageHitsTags(img, tags), spoilerBlockSimple); +} + +function blockComplexSpoiler(complex: AstMatcher, img: HTMLDivElement) { + return runFilter(img, imageHitsComplex(img, complex), spoilerBlockComplex); +} + +// --- + +function filterNode(node: Pick) { + const hiddenTags = getHiddenTags(), spoileredTags = getSpoileredTags(); + const { hiddenFilter, spoileredFilter } = window.booru; + + // Image thumb boxes with vote and fave buttons on them + $$('.image-container', node) + .filter(img => !thumbTagFilter(hiddenTags, img)) + .filter(img => !thumbComplexFilter(hiddenFilter, img)) + .filter(img => !thumbTagSpoiler(spoileredTags, img)) + .filter(img => !thumbComplexSpoiler(spoileredFilter, img)) + .forEach(img => showThumb(img)); + + // Individual image pages and images in posts/comments + $$('.image-show-container', node) + .filter(img => !blockTagFilter(hiddenTags, img)) + .filter(img => !blockComplexFilter(hiddenFilter, img)) + .filter(img => !blockTagSpoiler(spoileredTags, img)) + .filter(img => !blockComplexSpoiler(spoileredFilter, img)) + .forEach(img => showBlock(img)); +} + +function initImagesClientside() { + window.booru.imagesWithDownvotingDisabled = []; + // This fills the imagesWithDownvotingDisabled array + filterNode(document); + // Once the array is populated, we can initialize interactions + setupInteractions(); +} + +export { initImagesClientside, filterNode }; diff --git a/assets/js/pmwarning.js b/assets/js/pmwarning.ts similarity index 80% rename from assets/js/pmwarning.js rename to assets/js/pmwarning.ts index 104bd09c5..524f0f121 100644 --- a/assets/js/pmwarning.js +++ b/assets/js/pmwarning.ts @@ -7,8 +7,8 @@ import { $ } from './utils/dom'; function warnAboutPMs() { - const textarea = $('.js-toolbar-input'); - const warning = $('.js-hidden-warning'); + const textarea = $('.js-toolbar-input'); + const warning = $('.js-hidden-warning'); const imageEmbedRegex = /!+\[/g; if (!warning || !textarea) return; diff --git a/assets/js/poll.js b/assets/js/poll.ts similarity index 81% rename from assets/js/poll.js rename to assets/js/poll.ts index 68debf5d3..38c74907e 100644 --- a/assets/js/poll.js +++ b/assets/js/poll.ts @@ -1,6 +1,6 @@ import { inputDuplicatorCreator } from './input-duplicator'; -function pollOptionCreator() { +export function pollOptionCreator() { inputDuplicatorCreator({ addButtonSelector: '.js-poll-add-option', fieldSelector: '.js-poll-option', @@ -8,5 +8,3 @@ function pollOptionCreator() { removeButtonSelector: '.js-option-remove', }); } - -export { pollOptionCreator }; diff --git a/assets/js/search.js b/assets/js/search.js deleted file mode 100644 index 864ea2316..000000000 --- a/assets/js/search.js +++ /dev/null @@ -1,45 +0,0 @@ -import { $, $$ } from './utils/dom'; -import { addTag } from './tagsinput'; - -function showHelp(subject, type) { - $$('[data-search-help]').forEach(helpBox => { - if (helpBox.getAttribute('data-search-help') === type) { - $('.js-search-help-subject', helpBox).textContent = subject; - helpBox.classList.remove('hidden'); - } - else { - helpBox.classList.add('hidden'); - } - }); -} - -function prependToLast(field, value) { - const separatorIndex = field.value.lastIndexOf(','); - const advanceBy = field.value[separatorIndex + 1] === ' ' ? 2 : 1; - field.value = field.value.slice(0, separatorIndex + advanceBy) + value + field.value.slice(separatorIndex + advanceBy); -} - -function selectLast(field, characterCount) { - field.focus(); - - field.selectionStart = field.value.length - characterCount; - field.selectionEnd = field.value.length; -} - -function executeFormHelper(e) { - const searchField = $('.js-search-field'); - const attr = name => e.target.getAttribute(name); - - attr('data-search-add') && addTag(searchField, attr('data-search-add')); - attr('data-search-show-help') && showHelp(e.target.textContent, attr('data-search-show-help')); - attr('data-search-select-last') && selectLast(searchField, parseInt(attr('data-search-select-last'), 10)); - attr('data-search-prepend') && prependToLast(searchField, attr('data-search-prepend')); -} - -function setupSearch() { - const form = $('.js-search-form'); - - form && form.addEventListener('click', executeFormHelper); -} - -export { setupSearch }; diff --git a/assets/js/search.ts b/assets/js/search.ts new file mode 100644 index 000000000..068ff068f --- /dev/null +++ b/assets/js/search.ts @@ -0,0 +1,50 @@ +import { $, $$ } from './utils/dom'; +import { addTag } from './tagsinput'; + +function showHelp(subject: string, type: string | null) { + $$('[data-search-help]').forEach(helpBox => { + if (helpBox.getAttribute('data-search-help') === type) { + const searchSubject = $('.js-search-help-subject', helpBox); + + if (searchSubject) { + searchSubject.textContent = subject; + } + + helpBox.classList.remove('hidden'); + } + else { + helpBox.classList.add('hidden'); + } + }); +} + +function prependToLast(field: HTMLInputElement, value: string) { + const separatorIndex = field.value.lastIndexOf(','); + const advanceBy = field.value[separatorIndex + 1] === ' ' ? 2 : 1; + field.value = field.value.slice(0, separatorIndex + advanceBy) + value + field.value.slice(separatorIndex + advanceBy); +} + +function selectLast(field: HTMLInputElement, characterCount: number) { + field.focus(); + + field.selectionStart = field.value.length - characterCount; + field.selectionEnd = field.value.length; +} + +function executeFormHelper(e: PointerEvent) { + if (!e.target) { return; } + + const searchField = $('.js-search-field'); + const attr = (name: string) => e.target && (e.target as HTMLElement).getAttribute(name); + + attr('data-search-add') && addTag(searchField, attr('data-search-add')); + attr('data-search-show-help') && showHelp((e.target as Node).textContent || '', attr('data-search-show-help')); + attr('data-search-select-last') && searchField && selectLast(searchField, parseInt(attr('data-search-select-last') || '', 10)); + attr('data-search-prepend') && searchField && prependToLast(searchField, attr('data-search-prepend') || ''); +} + +export function setupSearch() { + const form = $('.js-search-form'); + + form && form.addEventListener('click', executeFormHelper as EventListener); +} diff --git a/assets/js/settings.js b/assets/js/settings.ts similarity index 50% rename from assets/js/settings.js rename to assets/js/settings.ts index 360228eea..d6b71d449 100644 --- a/assets/js/settings.js +++ b/assets/js/settings.ts @@ -6,12 +6,11 @@ import { $, $$ } from './utils/dom'; import store from './utils/store'; export function setupSettings() { + if (!$('#js-setting-table')) return; - if (!$('#js-setting-table')) return; - - const localCheckboxes = $$('[data-tab="local"] input[type="checkbox"]'); - const themeSelect = $('#user_theme'); - const styleSheet = $('head link[rel="stylesheet"]'); + const localCheckboxes = $$('[data-tab="local"] input[type="checkbox"]'); + const themeSelect = $('#user_theme'); + const styleSheet = $('head link[rel="stylesheet"]'); // Local settings localCheckboxes.forEach(checkbox => { @@ -22,7 +21,8 @@ export function setupSettings() { // Theme preview themeSelect && themeSelect.addEventListener('change', () => { - styleSheet.href = themeSelect.options[themeSelect.selectedIndex].dataset.themePath; + if (styleSheet) { + styleSheet.href = themeSelect.options[themeSelect.selectedIndex].dataset.themePath || '#'; + } }); - } diff --git a/assets/js/shortcuts.js b/assets/js/shortcuts.js deleted file mode 100644 index 67d1acbd2..000000000 --- a/assets/js/shortcuts.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Keyboard shortcuts - */ - -import { $ } from './utils/dom'; - -function getHover() { - const thumbBoxHover = $('.media-box:hover'); - if (thumbBoxHover) return thumbBoxHover.dataset.imageId; -} - -function openFullView() { - const imageHover = $('[data-uris]:hover'); - if (!imageHover) return; - - window.location = JSON.parse(imageHover.dataset.uris).full; -} - -function openFullViewNewTab() { - const imageHover = $('[data-uris]:hover'); - if (!imageHover) return; - - window.open(JSON.parse(imageHover.dataset.uris).full); -} - -function click(selector) { - const el = $(selector); - if (el) el.click(); -} - -function isOK(event) { - return !event.altKey && !event.ctrlKey && !event.metaKey && - document.activeElement.tagName !== 'INPUT' && - document.activeElement.tagName !== 'TEXTAREA'; -} - -const keyCodes = { - 74() { click('.js-prev'); }, // J - go to previous image - 73() { click('.js-up'); }, // I - go to index page - 75() { click('.js-next'); }, // K - go to next image - 82() { click('.js-rand'); }, // R - go to random image - 83() { click('.js-source-link'); }, // S - go to image source - 76() { click('.js-tag-sauce-toggle'); }, // L - edit tags - 79() { openFullView(); }, // O - open original - 86() { openFullViewNewTab(); }, // V - open original in a new tab - 70() { // F - favourite image - getHover() ? click(`a.interaction--fave[data-image-id="${getHover()}"]`) - : click('.block__header a.interaction--fave'); - }, - 85() { // U - upvote image - getHover() ? click(`a.interaction--upvote[data-image-id="${getHover()}"]`) - : click('.block__header a.interaction--upvote'); - }, -}; - -function listenForKeys() { - document.addEventListener('keydown', event => { - if (isOK(event) && keyCodes[event.keyCode]) { keyCodes[event.keyCode](); event.preventDefault(); } - }); -} - -export { listenForKeys }; diff --git a/assets/js/shortcuts.ts b/assets/js/shortcuts.ts new file mode 100644 index 000000000..c76a04585 --- /dev/null +++ b/assets/js/shortcuts.ts @@ -0,0 +1,76 @@ +/** + * Keyboard shortcuts + */ + +import { $ } from './utils/dom'; + +interface ShortcutKeycodes { + [key: string]: () => void +} + +function getHover(): string | null { + const thumbBoxHover = $('.media-box:hover'); + + return thumbBoxHover && (thumbBoxHover.dataset.imageId || null); +} + +function openFullView() { + const imageHover = $('[data-uris]:hover'); + + if (!imageHover || !imageHover.dataset.uris) return; + + window.location = JSON.parse(imageHover.dataset.uris).full; +} + +function openFullViewNewTab() { + const imageHover = $('[data-uris]:hover'); + + if (!imageHover || !imageHover.dataset.uris) return; + + window.open(JSON.parse(imageHover.dataset.uris).full); +} + +function click(selector: string) { + const el = $(selector); + + if (el) { + el.click(); + } +} + +function isOK(event: KeyboardEvent): boolean { + return !event.altKey && !event.ctrlKey && !event.metaKey && + document.activeElement !== null && + document.activeElement.tagName !== 'INPUT' && + document.activeElement.tagName !== 'TEXTAREA'; +} + +const keyCodes: ShortcutKeycodes = { + KeyJ() { click('.js-prev'); }, // J - go to previous image + KeyI() { click('.js-up'); }, // I - go to index page + KeyK() { click('.js-next'); }, // K - go to next image + KeyR() { click('.js-rand'); }, // R - go to random image + KeyS() { click('.js-source-link'); }, // S - go to image source + KeyL() { click('.js-tag-sauce-toggle'); }, // L - edit tags + KeyO() { openFullView(); }, // O - open original + KeyV() { openFullViewNewTab(); }, // V - open original in a new tab + KeyF() { // F - favourite image + getHover() ? click(`a.interaction--fave[data-image-id="${getHover()}"]`) + : click('.block__header a.interaction--fave'); + }, + KeyU() { // U - upvote image + getHover() ? click(`a.interaction--upvote[data-image-id="${getHover()}"]`) + : click('.block__header a.interaction--upvote'); + }, +}; + +function listenForKeys() { + document.addEventListener('keydown', (event: KeyboardEvent) => { + if (isOK(event) && keyCodes[event.code]) { + keyCodes[event.code](); + event.preventDefault(); + } + }); +} + +export { listenForKeys }; diff --git a/assets/js/sources.js b/assets/js/sources.ts similarity index 58% rename from assets/js/sources.js rename to assets/js/sources.ts index bb4ae3adc..b2c29c454 100644 --- a/assets/js/sources.js +++ b/assets/js/sources.ts @@ -1,5 +1,10 @@ +import { $ } from './utils/dom'; import { inputDuplicatorCreator } from './input-duplicator'; +export interface SourcesEvent extends CustomEvent { + target: HTMLElement, +} + function setupInputs() { inputDuplicatorCreator({ addButtonSelector: '.js-image-add-source', @@ -11,16 +16,17 @@ function setupInputs() { function imageSourcesCreator() { setupInputs(); - document.addEventListener('fetchcomplete', ({ target, detail }) => { - const sourceSauce = document.querySelector('.js-sourcesauce'); - if (target.matches('#source-form')) { + document.addEventListener('fetchcomplete', (({ target, detail }: SourcesEvent) => { + const sourceSauce = $('.js-sourcesauce'); + + if (sourceSauce && target && target.matches('#source-form')) { detail.text().then(text => { sourceSauce.outerHTML = text; setupInputs(); }); } - }); + }) as EventListener); } export { imageSourcesCreator }; diff --git a/assets/js/tagsmisc.js b/assets/js/tagsmisc.js deleted file mode 100644 index 29dc9dbe5..000000000 --- a/assets/js/tagsmisc.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Tags Misc - */ - -import { $$ } from './utils/dom'; -import store from './utils/store'; -import { initTagDropdown } from './tags'; -import { setupTagsInput, reloadTagsInput } from './tagsinput'; - -function tagInputButtons({target}) { - const actions = { - save(tagInput) { - store.set('tag_input', tagInput.value); - }, - load(tagInput) { - // If entry 'tag_input' does not exist, try to use the current list - tagInput.value = store.get('tag_input') || tagInput.value; - reloadTagsInput(tagInput); - }, - clear(tagInput) { - tagInput.value = ''; - reloadTagsInput(tagInput); - }, - }; - - for (const action in actions) { - if (target.matches(`#tagsinput-${action}`)) actions[action](document.getElementById('image_tag_input')); - } -} - -function setupTags() { - $$('.js-tag-block').forEach(el => { - setupTagsInput(el); - el.classList.remove('js-tag-block'); - }); -} - -function updateTagSauce({target, detail}) { - const tagSauce = document.querySelector('.js-tagsauce'); - - if (target.matches('#tags-form')) { - detail.text().then(text => { - tagSauce.outerHTML = text; - setupTags(); - initTagDropdown(); - }); - } -} - -function setupTagEvents() { - setupTags(); - document.addEventListener('fetchcomplete', updateTagSauce); - document.addEventListener('click', tagInputButtons); -} - -export { setupTagEvents }; diff --git a/assets/js/tagsmisc.ts b/assets/js/tagsmisc.ts new file mode 100644 index 000000000..ea5f49762 --- /dev/null +++ b/assets/js/tagsmisc.ts @@ -0,0 +1,70 @@ +/** + * Tags Misc + */ + +import { $, $$ } from './utils/dom'; +import store from './utils/store'; +import { initTagDropdown } from './tags'; +import { setupTagsInput, reloadTagsInput } from './tagsinput'; +import { SourcesEvent } from './sources'; + +type TagInputActionFunction = (tagInput: HTMLTextAreaElement | null) => void +type TagInputActionList = { + save: TagInputActionFunction, + load: TagInputActionFunction, + clear: TagInputActionFunction, +} + +function tagInputButtons({target}: PointerEvent) { + const actions: TagInputActionList = { + save(tagInput: HTMLTextAreaElement | null) { + tagInput && store.set('tag_input', tagInput.value); + }, + load(tagInput: HTMLTextAreaElement | null) { + if (!tagInput) { return; } + + // If entry 'tag_input' does not exist, try to use the current list + tagInput.value = store.get('tag_input') || tagInput.value; + reloadTagsInput(tagInput); + }, + clear(tagInput: HTMLTextAreaElement | null) { + if (!tagInput) { return; } + + tagInput.value = ''; + reloadTagsInput(tagInput); + }, + }; + + for (const action in actions) { + if (target && (target as HTMLElement).matches(`#tagsinput-${action}`)) { + actions[action as keyof TagInputActionList]($('image_tag_input')); + } + } +} + +function setupTags() { + $$('.js-tag-block').forEach(el => { + setupTagsInput(el); + el.classList.remove('js-tag-block'); + }); +} + +function updateTagSauce({target, detail}: SourcesEvent) { + const tagSauce = $('.js-tagsauce'); + + if (tagSauce && target.matches('#tags-form')) { + detail.text().then(text => { + tagSauce.outerHTML = text; + setupTags(); + initTagDropdown(); + }); + } +} + +function setupTagEvents() { + setupTags(); + document.addEventListener('fetchcomplete', updateTagSauce as EventListener); + document.addEventListener('click', tagInputButtons as EventListener); +} + +export { setupTagEvents }; diff --git a/assets/js/utils/tag.ts b/assets/js/utils/tag.ts index ea1042134..26c6bdf98 100644 --- a/assets/js/utils/tag.ts +++ b/assets/js/utils/tag.ts @@ -28,13 +28,13 @@ function sortTags(hidden: boolean, a: TagData, b: TagData): number { return a.spoiler_image_uri ? -1 : 1; } -export function getHiddenTags() { +export function getHiddenTags(): TagData[] { return unique(window.booru.hiddenTagList) .map(tagId => getTag(tagId)) .sort(sortTags.bind(null, true)); } -export function getSpoileredTags() { +export function getSpoileredTags(): TagData[] { if (window.booru.spoilerType === 'off') return []; return unique(window.booru.spoileredTagList) diff --git a/assets/js/when-ready.js b/assets/js/when-ready.ts similarity index 93% rename from assets/js/when-ready.js rename to assets/js/when-ready.ts index f39cec694..03cfee2a2 100644 --- a/assets/js/when-ready.js +++ b/assets/js/when-ready.ts @@ -4,9 +4,6 @@ import { whenReady } from './utils/dom'; -import { showOwnedComments } from './communications/comment'; -import { showOwnedPosts } from './communications/post'; - import { listenAutocomplete } from './autocomplete'; import { loadBooruData } from './booru'; import { registerEvents } from './boorujs'; @@ -40,8 +37,6 @@ import { setupSliders } from './slider'; whenReady(() => { - showOwnedComments(); - showOwnedPosts(); loadBooruData(); listenAutocomplete(); registerEvents(); diff --git a/assets/test/vitest-setup.ts b/assets/test/vitest-setup.ts index e4cd1314d..446beac6e 100644 --- a/assets/test/vitest-setup.ts +++ b/assets/test/vitest-setup.ts @@ -21,7 +21,8 @@ window.booru = { spoileredFilter: matchNone(), interactions: [], tagsVersion: 5, - hideStaffTools: 'false' + hideStaffTools: 'false', + galleryImages: [] }; // https://github.com/jsdom/jsdom/issues/1721#issuecomment-1484202038 diff --git a/assets/types/booru-object.d.ts b/assets/types/booru-object.d.ts index 2a5949c03..e23e7bce7 100644 --- a/assets/types/booru-object.d.ts +++ b/assets/types/booru-object.d.ts @@ -69,6 +69,10 @@ interface BooruObject { * Indicates whether sensitive staff-only info should be hidden or not. */ hideStaffTools: string; + /** + * List of image IDs in the current gallery. + */ + galleryImages: number[] } declare global { diff --git a/assets/vite.config.ts b/assets/vite.config.ts index e31cc1ef2..c677a7ff6 100644 --- a/assets/vite.config.ts +++ b/assets/vite.config.ts @@ -45,7 +45,7 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { cssCodeSplit: true, rollupOptions: { input: { - 'js/app': './js/app.js', + 'js/app': './js/app.ts', 'css/application': './css/application.css', ...Object.fromEntries(targets) }, diff --git a/lib/philomena_web/templates/layout/app.html.slime b/lib/philomena_web/templates/layout/app.html.slime index e529f4f56..a4a0e66cb 100644 --- a/lib/philomena_web/templates/layout/app.html.slime +++ b/lib/philomena_web/templates/layout/app.html.slime @@ -24,7 +24,7 @@ html lang="en" = vite_hmr? do script type="module" src="http://localhost:5173/@vite/client" - script type="module" src="http://localhost:5173/js/app.js" + script type="module" src="http://localhost:5173/js/app.ts" - else script type="text/javascript" src=Routes.static_path(@conn, "/js/app.js") async="async" = render PhilomenaWeb.LayoutView, "_opengraph.html", assigns From 4eeee71b48a9748ba73437df7ccc5ba76c80e606 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Thu, 6 Jun 2024 20:21:21 +0200 Subject: [PATCH 33/65] deleted comments styling --- assets/css/elements/button.css | 89 +++++++++++++++---- assets/css/views/communication.css | 25 ++++++ assets/css/views/pagination.css | 10 +-- assets/js/app.ts | 9 +- .../templates/comment/_comment.html.slime | 66 +++++++------- .../comment/_comment_options.html.slime | 38 ++++---- .../comment/_comment_with_image.html.slime | 62 ++++++------- .../templates/communication/_body.html.slime | 57 +++++++----- .../pagination/_pagination.html.slime | 24 ++--- 9 files changed, 240 insertions(+), 140 deletions(-) diff --git a/assets/css/elements/button.css b/assets/css/elements/button.css index 644f2b8f1..97df77e75 100644 --- a/assets/css/elements/button.css +++ b/assets/css/elements/button.css @@ -6,7 +6,7 @@ } .button--$(type) a { - color: var(--$(type)-link-color); + color: inherit; } .button--$(type).button--important { @@ -17,7 +17,8 @@ .button--$(type):hover, .button--$(type):active, .button--$(type).selected { - background: var(--$(type)-dark-color) !important; + color: var(--text-color) !important; + background: var(--$(type)-dark-color); border-radius: var(--border-radius-inner); } @@ -38,6 +39,63 @@ } } +@define-mixin button-block-type $type { + .block--$(type) .button:not([class*="button--"]) { + color: var(--$(type)-link-color) !important; + border-color: var(--$(type)-border-color); + background: var(--$(type)-muted-color); + } + + .block--$(type) .button:not([class*="button--"]) a { + color: inherit; + } + + .block--$(type) .button--important:not([class*="button--"]) { + color: var(--text-color) !important; + background: var(--$(type)-color); + } + + .block--$(type) .button:not([class*="button--"]):hover, + .block--$(type) .button:not([class*="button--"]):active, + .block--$(type) .button:not([class*="button--"]).selected { + color: var(--text-color) !important; + background: var(--$(type)-dark-color); + border-radius: var(--border-radius-inner); + } + + .block--$(type) .button__group:not([class*="button--"]), + .block--$(type) + .button__group--single:not([class*="button--"]) + .block--$(type) + .button__group--standalone:not([class*="button--"]) { + border-color: var(--$(type)-color); + background: var(--$(type)-dark-color); + } + + .block--$(type) .button__group:not([class*="button--"]) a, + .block--$(type) + .button__group--single:not([class*="button--"]) + a + .block--$(type) + .button__group--standalone:not([class*="button--"]) + a { + color: var(--$(type)-link-color); + } + + .block--$(type) .button__group:not([class*="button--"]) a:hover, + .block--$(type) + .button__group--single:not([class*="button--"]) + a:hover + .block--$(type) + .button__group--standalone:not([class*="button--"]) + a:hover { + @mixin animated-transition; + color: var(--text-color); + background: var(--$(type)-color); + border-radius: var(--border-radius-inner); + } +} + .button { @mixin animated-transition; display: flex; @@ -45,8 +103,8 @@ width: fit-content; font-weight: bold; font-size: var(--font-size); - background: var(--primary-dark-color); - color: var(--text-color); + background: var(--primary-muted-color); + color: var(--link-color); border-radius: var(--border-radius-inner); padding: 0 var(--padding-small); overflow: hidden; @@ -58,6 +116,7 @@ .button:hover, .button:active { @mixin animated-transition; + color: var(--text-color); background: var(--primary-dark-color); border-radius: var(--border-radius-inner); cursor: pointer; @@ -99,6 +158,7 @@ margin-right: var(--padding-normal); background: var(--secondary-dark-color); line-height: var(--button-group-height); + padding: 0 var(--padding-small); } .button__group:last-child, @@ -115,11 +175,6 @@ line-height: var(--button-group-small-height); } -.block__header__buttons .button { - color: var(--link-color); - border-width: 0 !important; -} - .button__group .button { border: 0; border-right: var(--secondary-color); @@ -145,15 +200,19 @@ border-width: 0; } -.block__header__buttons .button:hover { - background: var(--primary-muted-color); - border-radius: var(--border-radius-inner); -} - -.button--transparent { +.button--transparent, +.button--transparent:hover { background: 0 !important; } +@mixin button-block-type primary; +@mixin button-block-type secondary; +@mixin button-block-type success; +@mixin button-block-type warning; +@mixin button-block-type danger; +@mixin button-block-type information; +@mixin button-block-type special; + @mixin button-type primary; @mixin button-type secondary; @mixin button-type success; diff --git a/assets/css/views/communication.css b/assets/css/views/communication.css index c50ea77d0..241939f23 100644 --- a/assets/css/views/communication.css +++ b/assets/css/views/communication.css @@ -105,6 +105,19 @@ white-space: nowrap; } +.communication--deleted .communication__sender-block { + background: var(--danger-color); +} + +.communication__body__text--deleted { + display: flex; + flex-flow: column; + margin-top: var(--padding-normal); + background: var(--danger-muted-color); + padding: var(--padding-normal) var(--padding-small); + gap: var(--padding-small); +} + @mixin if-mobile { .communication__options { flex-flow: column; @@ -146,4 +159,16 @@ flex-direction: column; gap: var(--padding-small); } + + .communication__body__text--deleted { + margin-top: 0; + } + + .communication__body__text--deleted:first-child { + margin-bottom: var(--padding-normal); + } + + .communication--deleted > .communication__post-time { + background: var(--danger-dark-color); + } } diff --git a/assets/css/views/pagination.css b/assets/css/views/pagination.css index f384e966c..c028d41a0 100644 --- a/assets/css/views/pagination.css +++ b/assets/css/views/pagination.css @@ -4,22 +4,20 @@ line-height: inherit; } -.pagination span { - padding: 0 var(--padding-small); -} - +/* A hack to center icons */ .pagination i { font-size: 0.8rem !important; + height: 0.7rem; } .pagination a, .pagination span { display: grid; grid-template-columns: auto; - gap: var(--padding-tiny); + gap: var(--padding-small); align-items: center; - padding: 0 var(--padding-small); font-weight: bold; + padding: 0 var(--padding-small); } .pagination .with-icon { diff --git a/assets/js/app.ts b/assets/js/app.ts index 863f4ff24..bb01e5f3f 100644 --- a/assets/js/app.ts +++ b/assets/js/app.ts @@ -1,9 +1,8 @@ -// This is a manifest file that'll be compiled into including all the files listed below. -// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically -// be included in the compiled file accessible from http://example.com/assets/application.js -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// the compiled file. +// This is the Vite entry point of Philomena's clientside code. +// It is used as a sort of 'manifest' for what to include, and shouldn't +// have any code on its own. // +// Only edit this file as described by the comment about CSS development below. // Our code import './ujs'; diff --git a/lib/philomena_web/templates/comment/_comment.html.slime b/lib/philomena_web/templates/comment/_comment.html.slime index b0faefebc..127ebe7cc 100644 --- a/lib/philomena_web/templates/comment/_comment.html.slime +++ b/lib/philomena_web/templates/comment/_comment.html.slime @@ -1,6 +1,7 @@ - options = render PhilomenaWeb.CommentView, "_comment_link.html", comment: @comment, conn: @conn +- block_class = if @comment.hidden_from_users, do: "block--danger", else: "" -article.block.communication id="comment_#{@comment.id}" +article.block.communication id="comment_#{@comment.id}" class=block_class = if not @comment.approved and not @comment.hidden_from_users and (can?(@conn, :hide, @comment) or @comment.user_id == @conn.assigns.current_user.id) do .block__content .block.block--fixed.block--danger @@ -26,38 +27,39 @@ article.block.communication id="comment_#{@comment.id}" .block__content.flex.flex--no-wrap class=communication_body_class(@comment) = render PhilomenaWeb.CommunicationView, "_body.html", object: @comment, body: @body, conn: @conn, name: "comment", options: options - .block__content.communication__options - .flex.flex--wrap.flex--spaced-out - = render PhilomenaWeb.CommentView, "_comment_options.html", comment: @comment, conn: @conn - - = if can?(@conn, :hide, @comment) do - .flex__spacer - .js-staff-action - .communication__options__staff - = cond do - - @comment.hidden_from_users and not @comment.destroyed_content -> - = link(to: Routes.image_comment_hide_path(@conn, :delete, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "delete", class: "communication__interaction") do - i.fas.fa-check> - ' Restore - - = if can?(@conn, :delete, @comment) do - = link(to: Routes.image_comment_delete_path(@conn, :create, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "post", class: "communication__interaction") do - i.fas.fa-times> - ' Delete Contents + = if not @comment.hidden_from_users or can?(@conn, :edit, @comment) do + .block__content.communication__options + .flex.flex--wrap.flex--spaced-out + = render PhilomenaWeb.CommentView, "_comment_options.html", comment: @comment, conn: @conn + + = if can?(@conn, :hide, @comment) do + .flex__spacer + .js-staff-action + .communication__options__staff + = cond do + - @comment.hidden_from_users and not @comment.destroyed_content -> + = link(to: Routes.image_comment_hide_path(@conn, :delete, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "delete", class: "communication__interaction") do + i.fas.fa-check> + ' Restore - - not @comment.hidden_from_users and not @comment.destroyed_content -> - a.button.button--danger.button--transparent.togglable-delete-form-link href="#" data-click-toggle="#inline-del-form-comment-#{@comment.id}" - i.fas.fa-times> - ' Delete + = if can?(@conn, :delete, @comment) do + = link(to: Routes.image_comment_delete_path(@conn, :create, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "post", class: "communication__interaction") do + i.fas.fa-times> + ' Delete Contents + + - not @comment.hidden_from_users and not @comment.destroyed_content -> + a.button.button--danger.button--transparent.togglable-delete-form-link href="#" data-click-toggle="#inline-del-form-comment-#{@comment.id}" + i.fas.fa-times> + ' Delete - - true -> + - true -> - = if can?(@conn, :show, :ip_address) do - .button.button--warning.button--transparent.js-staff-action - =<> link_to_ip(@conn, @comment.ip) - .button.button--warning.button--transparent.js-staff-action - =<> link_to_fingerprint(@conn, @comment.fingerprint) + = if can?(@conn, :show, :ip_address) do + .button.button--warning.button--transparent.js-staff-action + =<> link_to_ip(@conn, @comment.ip) + .button.button--warning.button--transparent.js-staff-action + =<> link_to_fingerprint(@conn, @comment.fingerprint) - = form_for :comment, Routes.image_comment_hide_path(@conn, :create, @comment.image_id, @comment), [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> - = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-del-reason-comment-#{@comment.id}", required: true - = submit "Delete", class: "button" + = form_for :comment, Routes.image_comment_hide_path(@conn, :create, @comment.image_id, @comment), [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> + = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-del-reason-comment-#{@comment.id}", required: true + = submit "Delete", class: "button" diff --git a/lib/philomena_web/templates/comment/_comment_options.html.slime b/lib/philomena_web/templates/comment/_comment_options.html.slime index 32683808a..b662b9820 100644 --- a/lib/philomena_web/templates/comment/_comment_options.html.slime +++ b/lib/philomena_web/templates/comment/_comment_options.html.slime @@ -1,23 +1,23 @@ -.flex.flex--normal-gap.flex--centered - - link_path = Routes.image_path(@conn, :show, @comment.image) <> "#comment_#{@comment.id}" - - safe_author = PhilomenaWeb.PostView.markdown_safe_author(@comment) - - quote_body = if @comment.hidden_from_users, do: "", else: @comment.body += if not @comment.hidden_from_users or can?(@conn, :hide, @comment) do + .flex.flex--normal-gap.flex--centered + = if not @comment.hidden_from_users do + - link_path = Routes.image_path(@conn, :show, @comment.image) <> "#comment_#{@comment.id}" + - safe_author = PhilomenaWeb.PostView.markdown_safe_author(@comment) + - quote_body = if @comment.hidden_from_users, do: "", else: @comment.body - a.button.button--primary href=Routes.image_comment_report_path(@conn, :new, @comment.image, @comment) - i.fa.fa-flag> - ' Report + a.button href=Routes.image_comment_report_path(@conn, :new, @comment.image, @comment) + i.fa.fa-flag> + ' Report - a.button.button--primary.post-reply.post-reply-quote href=link_path data-reply-url=link_path data-author=safe_author data-post=quote_body - i.fa.fa-quote-right - ' Quote + a.button.post-reply.post-reply-quote href=link_path data-reply-url=link_path data-author=safe_author data-post=quote_body + i.fa.fa-quote-right + ' Quote - a.button.button--primary.post-reply href=link_path data-reply-url=link_path data-author=safe_author - i.fa.fa-reply - ' Reply + a.button.post-reply href=link_path data-reply-url=link_path data-author=safe_author + i.fa.fa-reply + ' Reply - = if can?(@conn, :edit, @comment) do - span.owner-options - strong - a.button.button--primary href=Routes.image_comment_path(@conn, :edit, @comment.image, @comment) - i.fas.fa-edit - ' Edit + = if not @comment.destroyed_content and can?(@conn, :edit, @comment) do + a.button href=Routes.image_comment_path(@conn, :edit, @comment.image, @comment) + i.fas.fa-edit + ' Edit diff --git a/lib/philomena_web/templates/comment/_comment_with_image.html.slime b/lib/philomena_web/templates/comment/_comment_with_image.html.slime index 0078f64fd..1ff5e0ab9 100644 --- a/lib/philomena_web/templates/comment/_comment_with_image.html.slime +++ b/lib/philomena_web/templates/comment/_comment_with_image.html.slime @@ -1,39 +1,41 @@ - options = render PhilomenaWeb.CommentView, "_comment_link.html", comment: @comment, conn: @conn +- block_class = if @comment.hidden_from_users, do: "block--danger", else: "" -article.block.communication id="comment_#{@comment.id}" +article.block.communication id="comment_#{@comment.id}" class=block_class .block__content.flex.flex--no-wrap class=communication_body_class(@comment) = render PhilomenaWeb.CommunicationView, "_body.html", object: @comment, image: @comment.image, body: @body, conn: @conn, name: "comment", options: options - .block__content.communication__options - .flex.flex--wrap.flex--spaced-out - = render PhilomenaWeb.CommentView, "_comment_options.html", comment: @comment, conn: @conn - - = if can?(@conn, :hide, @comment) do - .flex.flex--normal-gap.flex--centered.js-staff-action - = cond do - - @comment.hidden_from_users and not @comment.destroyed_content -> - = link(to: Routes.image_comment_hide_path(@conn, :delete, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "delete", class: "button button--success") do - i.fas.fa-check> - ' Restore - - = if can?(@conn, :delete, @comment) do - = link(to: Routes.image_comment_delete_path(@conn, :create, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "post", class: "button button--danger button--transparent") do + = if not @comment.hidden_from_users or can?(@conn, :edit, @comment) do + .block__content.communication__options + .flex.flex--wrap.flex--spaced-out + = render PhilomenaWeb.CommentView, "_comment_options.html", comment: @comment, conn: @conn + + = if can?(@conn, :hide, @comment) do + .flex.flex--normal-gap.flex--centered.js-staff-action + = cond do + - @comment.hidden_from_users and not @comment.destroyed_content -> + = link(to: Routes.image_comment_hide_path(@conn, :delete, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "delete", class: "button button--success") do + i.fas.fa-check> + ' Restore + + = if can?(@conn, :delete, @comment) do + = link(to: Routes.image_comment_delete_path(@conn, :create, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "post", class: "button button--danger button--transparent") do + i.fas.fa-times> + ' Delete Contents + + - not @comment.hidden_from_users and not @comment.destroyed_content -> + a.button.button--danger.button--transparent.togglable-delete-form-link href="#" data-click-toggle="#inline-del-form-comment-#{@comment.id}" i.fas.fa-times> - ' Delete Contents - - - not @comment.hidden_from_users and not @comment.destroyed_content -> - a.button.button--danger.button--transparent.togglable-delete-form-link href="#" data-click-toggle="#inline-del-form-comment-#{@comment.id}" - i.fas.fa-times> - ' Delete + ' Delete - - true -> + - true -> - = if can?(@conn, :show, :ip_address) do - .button.button--warning.button--transparent.js-staff-action - =<> link_to_ip(@conn, @comment.ip) - .button.button--warning.button--transparent.js-staff-action - =<> link_to_fingerprint(@conn, @comment.fingerprint) + = if can?(@conn, :show, :ip_address) do + .button.button--warning.button--transparent.js-staff-action + =<> link_to_ip(@conn, @comment.ip) + .button.button--warning.button--transparent.js-staff-action + =<> link_to_fingerprint(@conn, @comment.fingerprint) - = form_for :comment, Routes.image_comment_hide_path(@conn, :create, @comment.image_id, @comment), [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> - = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-del-reason-comment-#{@comment.id}", required: true - = submit "Delete", class: "button" + = form_for :comment, Routes.image_comment_hide_path(@conn, :create, @comment.image_id, @comment), [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> + = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-del-reason-comment-#{@comment.id}", required: true + = submit "Delete", class: "button" diff --git a/lib/philomena_web/templates/communication/_body.html.slime b/lib/philomena_web/templates/communication/_body.html.slime index 224d004d1..f2d293e1e 100644 --- a/lib/philomena_web/templates/communication/_body.html.slime +++ b/lib/philomena_web/templates/communication/_body.html.slime @@ -1,5 +1,6 @@ - anon = is_nil(assigns[:noanon]) or @noanon == false - options = if is_nil(assigns[:options]), do: "", else: @options +- deleted = Map.has_key?(@object, :hidden_from_users) and @object.hidden_from_users == true - avatar = cond do - not is_nil(assigns[:image]) -> @@ -20,27 +21,38 @@ - else = render PhilomenaWeb.UserAttributionView, "_user_title.html", object: @object, conn: @conn -- contents = if Map.has_key?(@object, :hidden_from_users) and @object.hidden_from_users == true do - strong.comment_deleted - ' Deletion reason: - =<> @object.deletion_reason +- contents = if deleted do + = if @object.destroyed_content and can?(@conn, :hide, @object) do + div.communication__body__text--deleted + span + i.fa.fa-eye-slash> + strong + ' Not shown to users, this + => @name + | is destroyed. + div.communication__body__text--deleted + span + i.fa.fa-trash-can> + strong This #{@name} has been deleted. + span + i.fa.fa-circle-info> + strong Reason: + =<> @object.deletion_reason = if can?(@conn, :hide, @object) and not is_nil(@object.deleted_by) do - | ( - = @object.deleted_by.name - | ) - = if can?(@conn, :hide, @object) do - = if @object.destroyed_content do - br - strong.comment_deleted> - | This #{@name}'s contents have been destroyed. - - else - br - =<> @body + span + i.fa.fa-user> + strong Moderator: + =<> @object.deleted_by.name + = if not @object.destroyed_content and can?(@conn, :hide, @object) do + =<> @body - else =<> @body -.flex.flex__grow.hidden--mobile - .flex.flex__fixed.spacing--right +- hidden_if_deleted = if deleted, do: "hidden", else: "" +- deleted_class = if deleted, do: "communication--deleted", else: "" + +.flex.flex__grow.hidden--mobile class=deleted_class + .flex.flex__fixed.spacing--right class=hidden_if_deleted = avatar .flex__grow.communication__body .communication__sender-block @@ -51,16 +63,19 @@ ' Posted =< pretty_time(@object.created_at) = options - = title + div class=hidden_if_deleted + = title .communication__body__text = contents -.flex.flex__column.flex__grow.hidden--desktop +.flex.flex__column.flex__grow.hidden--desktop class=deleted_class .communication__sender-block - = avatar + div class=hidden_if_deleted + = avatar .flex__column.flex--small-gap span.communication__sender-name = username - = title + div class=hidden_if_deleted + = title span.communication__post-time ' Posted = pretty_time(@object.created_at) diff --git a/lib/philomena_web/templates/pagination/_pagination.html.slime b/lib/philomena_web/templates/pagination/_pagination.html.slime index 055081880..577356311 100644 --- a/lib/philomena_web/templates/pagination/_pagination.html.slime +++ b/lib/philomena_web/templates/pagination/_pagination.html.slime @@ -4,11 +4,11 @@ nav.pagination.hidden--mobile = if not first_page?(@page) do = link to: first_page_path(@page, @route, params), class: "with-icon" do - i.fa.fa-backward> + i.fa.fa-angles-left> ' First .separator--vertical.separator--secondary = link to: prev_page_path(@page, @route, params), class: "js-prev with-icon" do - i.fa.fa-chevron-left> + i.fa.fa-angle-left> ' Prev .separator--vertical.separator--secondary @@ -32,29 +32,29 @@ .separator--vertical.separator--secondary = link to: next_page_path(@page, @route, params), class: "js-next with-icon" do ' Next - i.fa.fa-chevron-right + i.fa.fa-angle-right .separator--vertical.separator--secondary = link to: last_page_path(@page, @route, params), class: "with-icon" do ' Last - i.fa.fa-fast-forward + i.fa.fa-angles-right nav.pagination.hidden--desktop = if first_page?(@page) do span.with-icon - i.fa.fa-backward> + i.fa.fa-angles-left> ' First .separator--vertical.separator--secondary span.with-icon - i.fa.fa-chevron-left> + i.fa.fa-angle-left> ' Prev .separator--vertical.separator--secondary - else = link to: first_page_path(@page, @route, params), class: "with-icon" do - i.fa.fa-backward> + i.fa.fa-angles-left> ' First .separator--vertical.separator--secondary = link to: prev_page_path(@page, @route, params), class: "js-prev with-icon" do - i.fa.fa-chevron-left> + i.fa.fa-angle-left> ' Prev .separator--vertical.separator--secondary @@ -84,17 +84,17 @@ .separator--vertical.separator--secondary span.with-icon ' Next - i.fa.fa-chevron-right + i.fa.fa-angle-right .separator--vertical.separator--secondary span.with-icon ' Last - i.fa.fa-fast-forward + i.fa.fa-angles-right - else .separator--vertical.separator--secondary = link to: next_page_path(@page, @route, params), class: "js-next with-icon" do ' Next - i.fa.fa-chevron-right + i.fa.fa-angle-right .separator--vertical.separator--secondary = link to: last_page_path(@page, @route, params), class: "with-icon" do ' Last - i.fa.fa-fast-forward + i.fa.fa-angles-right From fd2d8462c2f6f50205a513d29b9f58fe54326488 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Thu, 6 Jun 2024 22:28:35 +0200 Subject: [PATCH 34/65] automatic route conversion --- .../admin/advert/image_controller.ex | 2 +- .../controllers/admin/advert_controller.ex | 6 +-- .../admin/artist_link/contact_controller.ex | 8 +-- .../admin/artist_link/reject_controller.ex | 8 +-- .../artist_link/verification_controller.ex | 8 +-- .../admin/badge/image_controller.ex | 2 +- .../controllers/admin/badge_controller.ex | 4 +- .../controllers/admin/batch/tag_controller.ex | 9 ++-- .../admin/dnp_entry/transition_controller.ex | 4 +- .../controllers/admin/donation_controller.ex | 4 +- .../admin/fingerprint_ban_controller.ex | 16 +++--- .../controllers/admin/forum_controller.ex | 4 +- .../controllers/admin/mod_note_controller.ex | 6 +-- .../admin/report/claim_controller.ex | 6 +-- .../admin/report/close_controller.ex | 2 +- .../admin/site_notice_controller.ex | 6 +-- .../admin/subnet_ban_controller.ex | 16 +++--- .../admin/user/activation_controller.ex | 4 +- .../admin/user/api_key_controller.ex | 2 +- .../admin/user/avatar_controller.ex | 2 +- .../admin/user/downvote_controller.ex | 2 +- .../admin/user/force_filter_controller.ex | 4 +- .../admin/user/unlock_controller.ex | 2 +- .../admin/user/verification_controller.ex | 12 ++--- .../controllers/admin/user/vote_controller.ex | 2 +- .../controllers/admin/user/wipe_controller.ex | 2 +- .../controllers/admin/user_ban_controller.ex | 16 +++--- .../controllers/admin/user_controller.ex | 8 +-- .../controllers/avatar_controller.ex | 4 +- .../controllers/channel/nsfw_controller.ex | 4 +- .../controllers/channel_controller.ex | 6 +-- .../controllers/confirmation_controller.ex | 2 +- .../conversation/hide_controller.ex | 4 +- .../message/approve_controller.ex | 4 +- .../conversation/message_controller.ex | 4 +- .../conversation/read_controller.ex | 4 +- .../conversation/report_controller.ex | 4 +- .../controllers/conversation_controller.ex | 2 +- .../controllers/dnp_entry_controller.ex | 4 +- .../duplicate_report/accept_controller.ex | 10 ++-- .../accept_reverse_controller.ex | 10 ++-- .../duplicate_report/claim_controller.ex | 12 ++--- .../duplicate_report/reject_controller.ex | 8 +-- .../duplicate_report_controller.ex | 4 +- .../controllers/filter/clear_recent.ex | 2 +- .../controllers/filter/public_controller.ex | 4 +- .../controllers/filter_controller.ex | 8 +-- .../controllers/gallery/report_controller.ex | 4 +- .../controllers/gallery_controller.ex | 6 +-- .../controllers/image/anonymous_controller.ex | 8 +-- .../controllers/image/approve_controller.ex | 8 +-- .../image/comment/approve_controller.ex | 8 +-- .../image/comment/delete_controller.ex | 14 ++---- .../image/comment/hide_controller.ex | 24 +++------ .../image/comment/report_controller.ex | 4 +- .../controllers/image/comment_controller.ex | 8 ++- .../image/comment_lock_controller.ex | 12 ++--- .../controllers/image/delete_controller.ex | 22 ++++---- .../image/description_lock_controller.ex | 12 ++--- .../controllers/image/destroy_controller.ex | 12 ++--- .../controllers/image/feature_controller.ex | 10 ++-- .../controllers/image/file_controller.ex | 6 +-- .../controllers/image/hash_controller.ex | 8 +-- .../controllers/image/navigate_controller.ex | 6 +-- .../controllers/image/random_controller.ex | 4 +- .../controllers/image/repair_controller.ex | 8 +-- .../controllers/image/report_controller.ex | 4 +- .../image/scratchpad_controller.ex | 8 +-- .../image/source_history_controller.ex | 8 +-- .../image/tag_change_controller.ex | 8 +-- .../controllers/image/tag_lock_controller.ex | 16 +++--- .../controllers/image/tamper_controller.ex | 12 ++--- .../controllers/image_controller.ex | 4 +- .../controllers/page_controller.ex | 4 +- .../controllers/password_controller.ex | 4 +- .../profile/artist_link_controller.ex | 8 +-- .../controllers/profile/award_controller.ex | 6 +-- .../profile/commission/item_controller.ex | 6 +-- .../profile/commission/report_controller.ex | 4 +- .../profile/commission_controller.ex | 8 +-- .../profile/description_controller.ex | 2 +- .../controllers/profile/report_controller.ex | 4 +- .../profile/scratchpad_controller.ex | 2 +- .../registration/email_controller.ex | 10 ++-- .../registration/name_controller.ex | 2 +- .../registration/password_controller.ex | 4 +- .../registration/totp_controller.ex | 4 +- .../controllers/registration_controller.ex | 2 +- .../controllers/report_controller.ex | 6 +-- .../controllers/session_controller.ex | 2 +- .../controllers/setting_controller.ex | 2 +- .../controllers/tag/alias_controller.ex | 4 +- .../controllers/tag/image_controller.ex | 12 ++--- .../controllers/tag/reindex_controller.ex | 2 +- .../controllers/tag_controller.ex | 12 ++--- .../controllers/topic/hide_controller.ex | 16 +++--- .../controllers/topic/lock_controller.ex | 16 +++--- .../controllers/topic/move_controller.ex | 10 ++-- .../controllers/topic/poll/vote_controller.ex | 8 +-- .../controllers/topic/poll_controller.ex | 4 +- .../topic/post/approve_controller.ex | 10 ++-- .../topic/post/delete_controller.ex | 10 ++-- .../controllers/topic/post/hide_controller.ex | 16 +++--- .../topic/post/report_controller.ex | 4 +- .../controllers/topic/post_controller.ex | 6 +-- .../controllers/topic/stick_controller.ex | 16 +++--- .../controllers/topic_controller.ex | 10 ++-- .../controllers/unlock_controller.ex | 2 +- lib/philomena_web/plugs/map_parameter_plug.ex | 2 +- .../plugs/moderation_log_plug.ex | 2 +- lib/philomena_web/plugs/tor_plug.ex | 3 +- lib/philomena_web/plugs/totp_plug.ex | 3 +- .../activity/_channel_strip.html.slime | 2 +- .../activity/_topic_strip.html.slime | 6 +-- .../templates/activity/index.html.slime | 4 +- .../templates/admin/advert/_form.html.slime | 2 +- .../templates/admin/advert/edit.html.slime | 2 +- .../admin/advert/image/edit.html.slime | 2 +- .../templates/admin/advert/index.html.slime | 8 +-- .../templates/admin/advert/new.html.slime | 2 +- .../admin/approval/_approvals.html.slime | 10 ++-- .../templates/admin/approval/index.html.slime | 2 +- .../admin/artist_link/index.html.slime | 18 +++---- .../templates/admin/badge/_form.html.slime | 2 +- .../templates/admin/badge/edit.html.slime | 4 +- .../admin/badge/image/edit.html.slime | 2 +- .../templates/admin/badge/index.html.slime | 8 +-- .../templates/admin/badge/new.html.slime | 4 +- .../admin/badge/user/index.html.slime | 4 +- .../admin/dnp_entry/index.html.slime | 36 ++++++------- .../admin/donation/_table.html.slime | 2 +- .../templates/admin/donation/index.html.slime | 2 +- .../admin/donation/user/index.html.slime | 2 +- .../admin/fingerprint_ban/edit.html.slime | 4 +- .../admin/fingerprint_ban/index.html.slime | 14 +++--- .../admin/fingerprint_ban/new.html.slime | 4 +- .../templates/admin/forum/edit.html.slime | 4 +- .../templates/admin/forum/index.html.slime | 6 +-- .../templates/admin/forum/new.html.slime | 4 +- .../admin/mod_note/_table.html.slime | 8 +-- .../templates/admin/mod_note/edit.html.slime | 2 +- .../templates/admin/mod_note/index.html.slime | 2 +- .../templates/admin/mod_note/new.html.slime | 2 +- .../admin/report/_reports.html.slime | 18 +++---- .../templates/admin/report/index.html.slime | 4 +- .../templates/admin/report/show.html.slime | 14 +++--- .../admin/site_notice/edit.html.slime | 2 +- .../admin/site_notice/index.html.slime | 8 +-- .../admin/site_notice/new.html.slime | 2 +- .../admin/subnet_ban/edit.html.slime | 4 +- .../admin/subnet_ban/index.html.slime | 14 +++--- .../templates/admin/subnet_ban/new.html.slime | 4 +- .../templates/admin/user/_form.html.slime | 2 +- .../templates/admin/user/edit.html.slime | 2 +- .../admin/user/force_filter/new.html.slime | 2 +- .../templates/admin/user/index.html.slime | 16 +++--- .../templates/admin/user_ban/edit.html.slime | 4 +- .../templates/admin/user_ban/index.html.slime | 14 +++--- .../templates/admin/user_ban/new.html.slime | 4 +- .../templates/advert/_box.html.slime | 2 +- .../templates/api/rss/watched/index.html.eex | 8 +-- .../templates/avatar/edit.html.slime | 8 +-- .../templates/channel/_channel_box.html.slime | 10 ++-- .../templates/channel/edit.html.slime | 2 +- .../templates/channel/index.html.slime | 10 ++-- .../templates/channel/new.html.slime | 2 +- .../subscription/_subscription.html.slime | 6 +-- .../templates/comment/_comment.html.slime | 10 ++-- .../comment/_comment_link.html.slime | 4 +- .../comment/_comment_options.html.slime | 6 +-- .../comment/_comment_with_image.html.slime | 6 +-- .../templates/comment/index.html.slime | 18 +++---- .../commission/_directory_results.html.slime | 8 +-- .../commission/_directory_sidebar.html.slime | 2 +- .../templates/confirmation/new.html.slime | 2 +- .../templates/conversation/index.html.slime | 10 ++-- .../conversation/message/_form.html.slime | 2 +- .../templates/conversation/new.html.slime | 4 +- .../templates/conversation/show.html.slime | 16 +++--- .../templates/dnp_entry/edit.html.slime | 2 +- .../templates/dnp_entry/index.html.slime | 10 ++-- .../templates/dnp_entry/new.html.slime | 2 +- .../templates/dnp_entry/show.html.slime | 22 ++++---- .../duplicate_report/_form.html.slime | 2 +- .../duplicate_report/_image_cell.html.slime | 4 +- .../duplicate_report/_list.html.slime | 8 +-- .../duplicate_report/index.html.slime | 18 +++---- .../templates/filter/_filter.html.slime | 8 +-- .../templates/filter/edit.html.slime | 4 +- .../templates/filter/index.html.slime | 32 ++++++------ .../templates/filter/new.html.slime | 2 +- .../templates/filter/show.html.slime | 8 +-- .../fingerprint_profile/show.html.slime | 22 ++++---- .../source_change/index.html.slime | 2 +- .../tag_change/index.html.slime | 8 +-- .../templates/forum/index.html.slime | 8 +-- .../templates/forum/show.html.slime | 14 +++--- .../subscription/_subscription.html.slime | 6 +-- .../templates/gallery/_gallery.html.slime | 2 +- .../templates/gallery/edit.html.slime | 2 +- .../templates/gallery/index.html.slime | 4 +- .../templates/gallery/new.html.slime | 2 +- .../templates/gallery/show.html.slime | 14 +++--- .../subscription/_subscription.html.slime | 6 +-- .../image/_add_to_gallery_dropdown.html.slime | 14 +++--- .../templates/image/_image_box.html.slime | 2 +- .../image/_image_container.html.slime | 2 +- .../templates/image/_image_meta.html.slime | 12 ++--- .../image/_image_mobile_navigation.html.slime | 8 +-- .../templates/image/_image_target.html.slime | 4 +- .../templates/image/_options.html.slime | 42 ++++++++-------- .../templates/image/_random_button.html.slime | 2 +- .../templates/image/_source.html.slime | 6 +-- .../templates/image/_tags.html.slime | 4 +- .../templates/image/_uploader.html.slime | 6 +-- .../templates/image/comment/_form.html.slime | 2 +- .../templates/image/comment/edit.html.slime | 2 +- .../image/comment/history/index.html.slime | 2 +- .../templates/image/comment/index.html.slime | 2 +- .../templates/image/deleted.html.slime | 2 +- .../image/description/_form.html.slime | 2 +- .../templates/image/favorite/index.html.slime | 12 ++--- .../templates/image/index.html.slime | 6 +-- .../templates/image/new.html.slime | 2 +- .../templates/image/related/index.html.slime | 2 +- .../templates/image/reporting/show.html.slime | 4 +- .../image/scratchpad/edit.html.slime | 4 +- .../templates/image/show.html.slime | 2 +- .../image/source_change/index.html.slime | 4 +- .../subscription/_subscription.html.slime | 6 +-- .../image/tag_change/index.html.slime | 10 ++-- .../templates/image/tag_lock/show.html.slime | 2 +- .../templates/ip_profile/show.html.slime | 22 ++++---- .../ip_profile/source_change/index.html.slime | 2 +- .../ip_profile/tag_change/index.html.slime | 8 +-- .../templates/layout/_header.html.slime | 22 ++++---- .../layout/_header_navigation.html.slime | 2 +- .../layout/_header_staff_links.html.slime | 32 ++++++------ .../templates/layout/_opengraph.html.slime | 6 +-- .../templates/layout/app.html.slime | 2 +- .../templates/message/_message.html.slime | 2 +- .../templates/moderation_log/index.html.slime | 2 +- .../notification/_channel.html.slime | 6 +-- .../templates/notification/_forum.html.slime | 8 +-- .../notification/_gallery.html.slime | 6 +-- .../templates/notification/_image.html.slime | 6 +-- .../templates/notification/_topic.html.slime | 6 +-- .../templates/notification/index.html.slime | 2 +- .../templates/page/edit.html.slime | 2 +- .../templates/page/history/index.html.slime | 4 +- .../templates/page/index.html.slime | 4 +- .../templates/page/new.html.slime | 2 +- .../templates/page/show.html.slime | 4 +- .../templates/password/edit.html.slime | 2 +- .../templates/password/new.html.slime | 2 +- .../templates/post/_post.html.slime | 14 +++--- .../templates/post/_post_options.html.slime | 6 +-- .../templates/post/index.html.slime | 32 ++++++------ .../templates/profile/_about_me.html.slime | 2 +- .../templates/profile/_admin_block.html.slime | 50 +++++++++---------- .../templates/profile/_commission.html.slime | 6 +-- .../profile/_recent_comments.html.slime | 2 +- .../profile/_recent_galleries.html.slime | 2 +- .../profile/_recent_posts.html.slime | 6 +-- .../profile/alias/_aliases.html.slime | 2 +- .../profile/artist_link/edit.html.slime | 2 +- .../profile/artist_link/index.html.slime | 4 +- .../profile/artist_link/new.html.slime | 2 +- .../profile/artist_link/show.html.slime | 10 ++-- .../templates/profile/award/edit.html.slime | 2 +- .../templates/profile/award/new.html.slime | 2 +- .../commission/_listing_items.html.slime | 8 +-- .../commission/_listing_sidebar.html.slime | 8 +-- .../profile/commission/edit.html.slime | 4 +- .../profile/commission/item/edit.html.slime | 4 +- .../profile/commission/item/new.html.slime | 4 +- .../profile/commission/new.html.slime | 4 +- .../profile/description/edit.html.slime | 2 +- .../templates/profile/detail/index.html.slime | 10 ++-- .../profile/fp_history/index.html.slime | 2 +- .../profile/ip_history/index.html.slime | 2 +- .../profile/scratchpad/edit.html.slime | 2 +- .../templates/profile/show.html.slime | 50 +++++++++---------- .../profile/source_change/index.html.slime | 4 +- .../profile/tag_change/index.html.slime | 12 ++--- .../templates/registration/edit.html.slime | 10 ++-- .../registration/name/edit.html.slime | 4 +- .../templates/registration/new.html.slime | 2 +- .../registration/totp/edit.html.slime | 4 +- .../templates/report/index.html.slime | 4 +- .../templates/report/new.html.slime | 2 +- .../templates/search/_form.html.slime | 2 +- .../templates/search/index.html.slime | 2 +- .../templates/search/reverse/index.html.slime | 4 +- .../templates/session/new.html.slime | 8 +-- .../templates/session/totp/new.html.slime | 2 +- .../templates/setting/edit.html.slime | 10 ++-- .../templates/source_change/index.html.slime | 2 +- .../templates/staff/index.html.slime | 4 +- .../templates/tag/_tag.html.slime | 12 ++--- .../templates/tag/_tag_info_row.html.slime | 24 ++++----- .../templates/tag/alias/edit.html.slime | 6 +-- .../templates/tag/detail/_filters.html.slime | 4 +- .../templates/tag/detail/index.html.slime | 4 +- .../templates/tag/edit.html.slime | 10 ++-- .../templates/tag/image/edit.html.slime | 6 +-- .../templates/tag/index.html.slime | 34 ++++++------- .../templates/tag/show.html.slime | 2 +- .../templates/tag/tag_change/index.html.slime | 10 ++-- .../templates/tag_change/index.html.slime | 6 +-- .../templates/topic/new.html.slime | 2 +- .../templates/topic/poll/_display.html.slime | 4 +- .../topic/poll/_vote_form.html.slime | 2 +- .../templates/topic/poll/edit.html.slime | 2 +- .../topic/poll/vote/index.html.slime | 4 +- .../templates/topic/post/_form.html.slime | 2 +- .../templates/topic/post/edit.html.slime | 2 +- .../topic/post/history/index.html.slime | 2 +- .../templates/topic/show.html.slime | 28 +++++------ .../subscription/_subscription.html.slime | 6 +-- .../templates/unlock/new.html.slime | 2 +- .../user_attribution/_anon_user.html.slime | 4 +- .../user_attribution/_user.html.slime | 2 +- lib/philomena_web/user_auth.ex | 3 +- .../views/admin/approval_view.ex | 2 +- lib/philomena_web/views/admin/ban_view.ex | 4 +- .../views/admin/mod_note_view.ex | 18 +++---- lib/philomena_web/views/admin/report_view.ex | 14 +++--- lib/philomena_web/views/conversation_view.ex | 4 +- lib/philomena_web/views/profile_view.ex | 44 +++++++++++----- lib/philomena_web/views/report_view.ex | 30 +++++------ lib/philomena_web/views/setting_view.ex | 2 +- .../confirmation_controller_test.exs | 14 +++--- .../controllers/password_controller_test.exs | 18 +++---- .../registration/email_controller_test.exs | 22 ++++---- .../registration/password_controller_test.exs | 8 +-- .../registration_controller_test.exs | 14 +++--- .../controllers/session_controller_test.exs | 12 ++--- .../controllers/unlock_controller_test.exs | 14 +++--- test/philomena_web/user_auth_test.exs | 2 +- 340 files changed, 1218 insertions(+), 1224 deletions(-) diff --git a/lib/philomena_web/controllers/admin/advert/image_controller.ex b/lib/philomena_web/controllers/admin/advert/image_controller.ex index 8fe46022f..c3bae8c08 100644 --- a/lib/philomena_web/controllers/admin/advert/image_controller.ex +++ b/lib/philomena_web/controllers/admin/advert/image_controller.ex @@ -22,7 +22,7 @@ defmodule PhilomenaWeb.Admin.Advert.ImageController do {:ok, _advert} -> conn |> put_flash(:info, "Advert was successfully updated.") - |> redirect(to: Routes.admin_advert_path(conn, :index)) + |> redirect(to: ~p"/admin/adverts") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/admin/advert_controller.ex b/lib/philomena_web/controllers/admin/advert_controller.ex index bb119f427..e058ce26f 100644 --- a/lib/philomena_web/controllers/admin/advert_controller.ex +++ b/lib/philomena_web/controllers/admin/advert_controller.ex @@ -32,7 +32,7 @@ defmodule PhilomenaWeb.Admin.AdvertController do {:ok, _advert} -> conn |> put_flash(:info, "Advert was successfully created.") - |> redirect(to: Routes.admin_advert_path(conn, :index)) + |> redirect(to: ~p"/admin/adverts") {:error, :advert, changeset, _changes} -> render(conn, "new.html", changeset: changeset) @@ -49,7 +49,7 @@ defmodule PhilomenaWeb.Admin.AdvertController do {:ok, _advert} -> conn |> put_flash(:info, "Advert was successfully updated.") - |> redirect(to: Routes.admin_advert_path(conn, :index)) + |> redirect(to: ~p"/admin/adverts") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -61,7 +61,7 @@ defmodule PhilomenaWeb.Admin.AdvertController do conn |> put_flash(:info, "Advert was successfully deleted.") - |> redirect(to: Routes.admin_advert_path(conn, :index)) + |> redirect(to: ~p"/admin/adverts") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/artist_link/contact_controller.ex b/lib/philomena_web/controllers/admin/artist_link/contact_controller.ex index 98efb1f43..df25d5bec 100644 --- a/lib/philomena_web/controllers/admin/artist_link/contact_controller.ex +++ b/lib/philomena_web/controllers/admin/artist_link/contact_controller.ex @@ -18,14 +18,14 @@ defmodule PhilomenaWeb.Admin.ArtistLink.ContactController do conn |> put_flash(:info, "Artist successfully marked as contacted.") - |> moderation_log(details: &log_details/3, data: artist_link) - |> redirect(to: Routes.admin_artist_link_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: artist_link) + |> redirect(to: ~p"/admin/artist_links") end - defp log_details(conn, _action, artist_link) do + defp log_details(_action, artist_link) do %{ body: "Contacted artist #{artist_link.user.name} at #{artist_link.uri}", - subject_path: Routes.profile_artist_link_path(conn, :show, artist_link.user, artist_link) + subject_path: ~p"/profiles/#{artist_link.user}/artist_links/#{artist_link}" } end end diff --git a/lib/philomena_web/controllers/admin/artist_link/reject_controller.ex b/lib/philomena_web/controllers/admin/artist_link/reject_controller.ex index 9bf921394..b9aab7121 100644 --- a/lib/philomena_web/controllers/admin/artist_link/reject_controller.ex +++ b/lib/philomena_web/controllers/admin/artist_link/reject_controller.ex @@ -17,14 +17,14 @@ defmodule PhilomenaWeb.Admin.ArtistLink.RejectController do conn |> put_flash(:info, "Artist link successfully marked as rejected.") - |> moderation_log(details: &log_details/3, data: artist_link) - |> redirect(to: Routes.admin_artist_link_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: artist_link) + |> redirect(to: ~p"/admin/artist_links") end - defp log_details(conn, _action, artist_link) do + defp log_details(_action, artist_link) do %{ body: "Rejected artist link #{artist_link.uri} created by #{artist_link.user.name}", - subject_path: Routes.profile_artist_link_path(conn, :show, artist_link.user, artist_link) + subject_path: ~p"/profiles/#{artist_link.user}/artist_links/#{artist_link}" } end end diff --git a/lib/philomena_web/controllers/admin/artist_link/verification_controller.ex b/lib/philomena_web/controllers/admin/artist_link/verification_controller.ex index a4cc1fcee..a1a2f86a6 100644 --- a/lib/philomena_web/controllers/admin/artist_link/verification_controller.ex +++ b/lib/philomena_web/controllers/admin/artist_link/verification_controller.ex @@ -18,14 +18,14 @@ defmodule PhilomenaWeb.Admin.ArtistLink.VerificationController do conn |> put_flash(:info, "Artist link successfully verified.") - |> moderation_log(details: &log_details/3, data: result.artist_link) - |> redirect(to: Routes.admin_artist_link_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: result.artist_link) + |> redirect(to: ~p"/admin/artist_links") end - defp log_details(conn, _action, artist_link) do + defp log_details(_action, artist_link) do %{ body: "Verified artist link #{artist_link.uri} created by #{artist_link.user.name}", - subject_path: Routes.profile_artist_link_path(conn, :show, artist_link.user, artist_link) + subject_path: ~p"/profiles/#{artist_link.user}/artist_links/#{artist_link}" } end end diff --git a/lib/philomena_web/controllers/admin/badge/image_controller.ex b/lib/philomena_web/controllers/admin/badge/image_controller.ex index abdac0a23..069b8f0b9 100644 --- a/lib/philomena_web/controllers/admin/badge/image_controller.ex +++ b/lib/philomena_web/controllers/admin/badge/image_controller.ex @@ -17,7 +17,7 @@ defmodule PhilomenaWeb.Admin.Badge.ImageController do {:ok, _badge} -> conn |> put_flash(:info, "Badge updated successfully.") - |> redirect(to: Routes.admin_badge_path(conn, :index)) + |> redirect(to: ~p"/admin/badges") {:error, :badge, changeset, _changes} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/admin/badge_controller.ex b/lib/philomena_web/controllers/admin/badge_controller.ex index 2aa79e319..f253f6476 100644 --- a/lib/philomena_web/controllers/admin/badge_controller.ex +++ b/lib/philomena_web/controllers/admin/badge_controller.ex @@ -28,7 +28,7 @@ defmodule PhilomenaWeb.Admin.BadgeController do {:ok, _badge} -> conn |> put_flash(:info, "Badge created successfully.") - |> redirect(to: Routes.admin_badge_path(conn, :index)) + |> redirect(to: ~p"/admin/badges") {:error, :badge, changeset, _changes} -> render(conn, "new.html", changeset: changeset) @@ -45,7 +45,7 @@ defmodule PhilomenaWeb.Admin.BadgeController do {:ok, _badge} -> conn |> put_flash(:info, "Badge updated successfully.") - |> redirect(to: Routes.admin_badge_path(conn, :index)) + |> redirect(to: ~p"/admin/badges") {:error, :badge, changeset, _changes} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/admin/batch/tag_controller.ex b/lib/philomena_web/controllers/admin/batch/tag_controller.ex index 4ae3c7140..ec98b530e 100644 --- a/lib/philomena_web/controllers/admin/batch/tag_controller.ex +++ b/lib/philomena_web/controllers/admin/batch/tag_controller.ex @@ -48,10 +48,11 @@ defmodule PhilomenaWeb.Admin.Batch.TagController do {:ok, _} -> conn |> moderation_log( - details: &log_details/3, + details: &log_details/2, data: %{ tag_list: tag_list, - image_count: Enum.count(image_ids) + image_count: Enum.count(image_ids), + user: conn.assigns.current_user } ) |> json(%{succeeded: image_ids, failed: []}) @@ -68,10 +69,10 @@ defmodule PhilomenaWeb.Admin.Batch.TagController do end end - defp log_details(conn, _action, data) do + defp log_details(_action, data) do %{ body: "Batch tagged '#{data.tag_list}' on #{data.image_count} images", - subject_path: Routes.profile_path(conn, :show, conn.assigns.current_user) + subject_path: ~p"/profiles/#{data.user}" } end end diff --git a/lib/philomena_web/controllers/admin/dnp_entry/transition_controller.ex b/lib/philomena_web/controllers/admin/dnp_entry/transition_controller.ex index a774ece0c..8f0fc52e0 100644 --- a/lib/philomena_web/controllers/admin/dnp_entry/transition_controller.ex +++ b/lib/philomena_web/controllers/admin/dnp_entry/transition_controller.ex @@ -16,12 +16,12 @@ defmodule PhilomenaWeb.Admin.DnpEntry.TransitionController do {:ok, dnp_entry} -> conn |> put_flash(:info, "Successfully updated DNP entry.") - |> redirect(to: Routes.dnp_entry_path(conn, :show, dnp_entry)) + |> redirect(to: ~p"/dnp/#{dnp_entry}") {:error, _changeset} -> conn |> put_flash(:error, "Failed to update DNP entry!") - |> redirect(to: Routes.dnp_entry_path(conn, :show, conn.assigns.dnp_entry)) + |> redirect(to: ~p"/dnp/#{conn.assigns.dnp_entry}") end end diff --git a/lib/philomena_web/controllers/admin/donation_controller.ex b/lib/philomena_web/controllers/admin/donation_controller.ex index 91de59cf8..2274daff4 100644 --- a/lib/philomena_web/controllers/admin/donation_controller.ex +++ b/lib/philomena_web/controllers/admin/donation_controller.ex @@ -23,12 +23,12 @@ defmodule PhilomenaWeb.Admin.DonationController do {:ok, _donation} -> conn |> put_flash(:info, "Donation successfully created.") - |> redirect(to: Routes.admin_donation_path(conn, :index)) + |> redirect(to: ~p"/admin/donations") _error -> conn |> put_flash(:error, "Error creating donation!") - |> redirect(to: Routes.admin_donation_path(conn, :index)) + |> redirect(to: ~p"/admin/donations") end end diff --git a/lib/philomena_web/controllers/admin/fingerprint_ban_controller.ex b/lib/philomena_web/controllers/admin/fingerprint_ban_controller.ex index 5cb4ccd07..ccc4f56aa 100644 --- a/lib/philomena_web/controllers/admin/fingerprint_ban_controller.ex +++ b/lib/philomena_web/controllers/admin/fingerprint_ban_controller.ex @@ -47,8 +47,8 @@ defmodule PhilomenaWeb.Admin.FingerprintBanController do {:ok, fingerprint_ban} -> conn |> put_flash(:info, "Fingerprint was successfully banned.") - |> moderation_log(details: &log_details/3, data: fingerprint_ban) - |> redirect(to: Routes.admin_fingerprint_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: fingerprint_ban) + |> redirect(to: ~p"/admin/fingerprint_bans") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -65,8 +65,8 @@ defmodule PhilomenaWeb.Admin.FingerprintBanController do {:ok, fingerprint_ban} -> conn |> put_flash(:info, "Fingerprint ban successfully updated.") - |> moderation_log(details: &log_details/3, data: fingerprint_ban) - |> redirect(to: Routes.admin_fingerprint_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: fingerprint_ban) + |> redirect(to: ~p"/admin/fingerprint_bans") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -78,8 +78,8 @@ defmodule PhilomenaWeb.Admin.FingerprintBanController do conn |> put_flash(:info, "Fingerprint ban successfully deleted.") - |> moderation_log(details: &log_details/3, data: fingerprint_ban) - |> redirect(to: Routes.admin_fingerprint_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: fingerprint_ban) + |> redirect(to: ~p"/admin/fingerprint_bans") end defp load_bans(queryable, conn) do @@ -110,7 +110,7 @@ defmodule PhilomenaWeb.Admin.FingerprintBanController do end end - defp log_details(conn, action, ban) do + defp log_details(action, ban) do body = case action do :create -> "Created a fingerprint ban #{ban.generated_ban_id}" @@ -118,6 +118,6 @@ defmodule PhilomenaWeb.Admin.FingerprintBanController do :delete -> "Deleted a fingerprint ban #{ban.generated_ban_id}" end - %{body: body, subject_path: Routes.admin_fingerprint_ban_path(conn, :index)} + %{body: body, subject_path: ~p"/admin/fingerprint_bans"} end end diff --git a/lib/philomena_web/controllers/admin/forum_controller.ex b/lib/philomena_web/controllers/admin/forum_controller.ex index 7d9e01900..5c40e4c4a 100644 --- a/lib/philomena_web/controllers/admin/forum_controller.ex +++ b/lib/philomena_web/controllers/admin/forum_controller.ex @@ -21,7 +21,7 @@ defmodule PhilomenaWeb.Admin.ForumController do {:ok, _forum} -> conn |> put_flash(:info, "Forum created successfully.") - |> redirect(to: Routes.admin_forum_path(conn, :index)) + |> redirect(to: ~p"/admin/forums") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -38,7 +38,7 @@ defmodule PhilomenaWeb.Admin.ForumController do {:ok, _forum} -> conn |> put_flash(:info, "Forum updated successfully.") - |> redirect(to: Routes.admin_forum_path(conn, :index)) + |> redirect(to: ~p"/admin/forums") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/admin/mod_note_controller.ex b/lib/philomena_web/controllers/admin/mod_note_controller.ex index 23f71815a..f5b3999f1 100644 --- a/lib/philomena_web/controllers/admin/mod_note_controller.ex +++ b/lib/philomena_web/controllers/admin/mod_note_controller.ex @@ -46,7 +46,7 @@ defmodule PhilomenaWeb.Admin.ModNoteController do {:ok, _mod_note} -> conn |> put_flash(:info, "Successfully created mod note.") - |> redirect(to: Routes.admin_mod_note_path(conn, :index)) + |> redirect(to: ~p"/admin/mod_notes") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -63,7 +63,7 @@ defmodule PhilomenaWeb.Admin.ModNoteController do {:ok, _mod_note} -> conn |> put_flash(:info, "Successfully updated mod note.") - |> redirect(to: Routes.admin_mod_note_path(conn, :index)) + |> redirect(to: ~p"/admin/mod_notes") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -75,7 +75,7 @@ defmodule PhilomenaWeb.Admin.ModNoteController do conn |> put_flash(:info, "Successfully deleted mod note.") - |> redirect(to: Routes.admin_mod_note_path(conn, :index)) + |> redirect(to: ~p"/admin/mod_notes") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/report/claim_controller.ex b/lib/philomena_web/controllers/admin/report/claim_controller.ex index ffcdfcc61..fed9314b7 100644 --- a/lib/philomena_web/controllers/admin/report/claim_controller.ex +++ b/lib/philomena_web/controllers/admin/report/claim_controller.ex @@ -12,12 +12,12 @@ defmodule PhilomenaWeb.Admin.Report.ClaimController do {:ok, _report} -> conn |> put_flash(:info, "Successfully marked report as in progress") - |> redirect(to: Routes.admin_report_path(conn, :index)) + |> redirect(to: ~p"/admin/reports") {:error, _changeset} -> conn |> put_flash(:warning, "Couldn't claim that report!") - |> redirect(to: Routes.admin_report_path(conn, :show, conn.assigns.report)) + |> redirect(to: ~p"/admin/reports/#{conn.assigns.report}") end end @@ -26,6 +26,6 @@ defmodule PhilomenaWeb.Admin.Report.ClaimController do conn |> put_flash(:info, "Successfully released report.") - |> redirect(to: Routes.admin_report_path(conn, :show, report)) + |> redirect(to: ~p"/admin/reports/#{report}") end end diff --git a/lib/philomena_web/controllers/admin/report/close_controller.ex b/lib/philomena_web/controllers/admin/report/close_controller.ex index dedb4d2d4..75f1e7ff5 100644 --- a/lib/philomena_web/controllers/admin/report/close_controller.ex +++ b/lib/philomena_web/controllers/admin/report/close_controller.ex @@ -12,6 +12,6 @@ defmodule PhilomenaWeb.Admin.Report.CloseController do conn |> put_flash(:info, "Successfully closed report") - |> redirect(to: Routes.admin_report_path(conn, :index)) + |> redirect(to: ~p"/admin/reports") end end diff --git a/lib/philomena_web/controllers/admin/site_notice_controller.ex b/lib/philomena_web/controllers/admin/site_notice_controller.ex index 11462540b..7612422eb 100644 --- a/lib/philomena_web/controllers/admin/site_notice_controller.ex +++ b/lib/philomena_web/controllers/admin/site_notice_controller.ex @@ -28,7 +28,7 @@ defmodule PhilomenaWeb.Admin.SiteNoticeController do {:ok, _site_notice} -> conn |> put_flash(:info, "Successfully created site notice.") - |> redirect(to: Routes.admin_site_notice_path(conn, :index)) + |> redirect(to: ~p"/admin/site_notices") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -45,7 +45,7 @@ defmodule PhilomenaWeb.Admin.SiteNoticeController do {:ok, _site_notice} -> conn |> put_flash(:info, "Succesfully updated site notice.") - |> redirect(to: Routes.admin_site_notice_path(conn, :index)) + |> redirect(to: ~p"/admin/site_notices") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -57,7 +57,7 @@ defmodule PhilomenaWeb.Admin.SiteNoticeController do conn |> put_flash(:info, "Sucessfully deleted site notice.") - |> redirect(to: Routes.admin_site_notice_path(conn, :index)) + |> redirect(to: ~p"/admin/site_notices") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/subnet_ban_controller.ex b/lib/philomena_web/controllers/admin/subnet_ban_controller.ex index 93c42807e..f41639216 100644 --- a/lib/philomena_web/controllers/admin/subnet_ban_controller.ex +++ b/lib/philomena_web/controllers/admin/subnet_ban_controller.ex @@ -49,8 +49,8 @@ defmodule PhilomenaWeb.Admin.SubnetBanController do {:ok, subnet_ban} -> conn |> put_flash(:info, "Subnet was successfully banned.") - |> moderation_log(details: &log_details/3, data: subnet_ban) - |> redirect(to: Routes.admin_subnet_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: subnet_ban) + |> redirect(to: ~p"/admin/subnet_bans") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -67,8 +67,8 @@ defmodule PhilomenaWeb.Admin.SubnetBanController do {:ok, subnet_ban} -> conn |> put_flash(:info, "Subnet ban successfully updated.") - |> moderation_log(details: &log_details/3, data: subnet_ban) - |> redirect(to: Routes.admin_subnet_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: subnet_ban) + |> redirect(to: ~p"/admin/subnet_bans") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -80,8 +80,8 @@ defmodule PhilomenaWeb.Admin.SubnetBanController do conn |> put_flash(:info, "Subnet ban successfully deleted.") - |> moderation_log(details: &log_details/3, data: subnet_ban) - |> redirect(to: Routes.admin_subnet_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: subnet_ban) + |> redirect(to: ~p"/admin/subnet_bans") end defp load_bans(queryable, conn) do @@ -112,7 +112,7 @@ defmodule PhilomenaWeb.Admin.SubnetBanController do end end - defp log_details(conn, action, ban) do + defp log_details(action, ban) do body = case action do :create -> "Created a subnet ban #{ban.generated_ban_id}" @@ -120,6 +120,6 @@ defmodule PhilomenaWeb.Admin.SubnetBanController do :delete -> "Deleted a subnet ban #{ban.generated_ban_id}" end - %{body: body, subject_path: Routes.admin_subnet_ban_path(conn, :index)} + %{body: body, subject_path: ~p"/admin/subnet_bans"} end end diff --git a/lib/philomena_web/controllers/admin/user/activation_controller.ex b/lib/philomena_web/controllers/admin/user/activation_controller.ex index e5f78aad2..f261ab1bc 100644 --- a/lib/philomena_web/controllers/admin/user/activation_controller.ex +++ b/lib/philomena_web/controllers/admin/user/activation_controller.ex @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.Admin.User.ActivationController do conn |> put_flash(:info, "User was reactivated.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") end def delete(conn, _params) do @@ -20,7 +20,7 @@ defmodule PhilomenaWeb.Admin.User.ActivationController do conn |> put_flash(:info, "User was deactivated.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user/api_key_controller.ex b/lib/philomena_web/controllers/admin/user/api_key_controller.ex index 7059779d5..56414283f 100644 --- a/lib/philomena_web/controllers/admin/user/api_key_controller.ex +++ b/lib/philomena_web/controllers/admin/user/api_key_controller.ex @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.Admin.User.ApiKeyController do conn |> put_flash(:info, "API token was successfully reset.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user/avatar_controller.ex b/lib/philomena_web/controllers/admin/user/avatar_controller.ex index c04fdbcd7..80c1e2629 100644 --- a/lib/philomena_web/controllers/admin/user/avatar_controller.ex +++ b/lib/philomena_web/controllers/admin/user/avatar_controller.ex @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.Admin.User.AvatarController do conn |> put_flash(:info, "Successfully removed avatar.") - |> redirect(to: Routes.admin_user_path(conn, :edit, conn.assigns.user)) + |> redirect(to: ~p"/admin/users/#{conn.assigns.user}/edit") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user/downvote_controller.ex b/lib/philomena_web/controllers/admin/user/downvote_controller.ex index ec52452aa..f2694da51 100644 --- a/lib/philomena_web/controllers/admin/user/downvote_controller.ex +++ b/lib/philomena_web/controllers/admin/user/downvote_controller.ex @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.Admin.User.DownvoteController do conn |> put_flash(:alert, "Downvote wipe started.") - |> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user/force_filter_controller.ex b/lib/philomena_web/controllers/admin/user/force_filter_controller.ex index 049d68fbc..74266ef5e 100644 --- a/lib/philomena_web/controllers/admin/user/force_filter_controller.ex +++ b/lib/philomena_web/controllers/admin/user/force_filter_controller.ex @@ -18,7 +18,7 @@ defmodule PhilomenaWeb.Admin.User.ForceFilterController do conn |> put_flash(:info, "Filter was forced.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") end def delete(conn, _params) do @@ -26,7 +26,7 @@ defmodule PhilomenaWeb.Admin.User.ForceFilterController do conn |> put_flash(:info, "Forced filter was removed.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user/unlock_controller.ex b/lib/philomena_web/controllers/admin/user/unlock_controller.ex index 0c36122ec..54610fdaa 100644 --- a/lib/philomena_web/controllers/admin/user/unlock_controller.ex +++ b/lib/philomena_web/controllers/admin/user/unlock_controller.ex @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.Admin.User.UnlockController do conn |> put_flash(:info, "User was unlocked.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user/verification_controller.ex b/lib/philomena_web/controllers/admin/user/verification_controller.ex index 982a520cd..82b0745ad 100644 --- a/lib/philomena_web/controllers/admin/user/verification_controller.ex +++ b/lib/philomena_web/controllers/admin/user/verification_controller.ex @@ -12,8 +12,8 @@ defmodule PhilomenaWeb.Admin.User.VerificationController do conn |> put_flash(:info, "User verification granted.") - |> moderation_log(details: &log_details/3, data: user) - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> moderation_log(details: &log_details/2, data: user) + |> redirect(to: ~p"/profiles/#{user}") end def delete(conn, _params) do @@ -21,8 +21,8 @@ defmodule PhilomenaWeb.Admin.User.VerificationController do conn |> put_flash(:info, "User verification revoked.") - |> moderation_log(details: &log_details/3, data: user) - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> moderation_log(details: &log_details/2, data: user) + |> redirect(to: ~p"/profiles/#{user}") end defp verify_authorized(conn, _opts) do @@ -32,13 +32,13 @@ defmodule PhilomenaWeb.Admin.User.VerificationController do end end - defp log_details(conn, action, user) do + defp log_details(action, user) do body = case action do :create -> "Granted verification to #{user.name}" :delete -> "Revoked verification from #{user.name}" end - %{body: body, subject_path: Routes.profile_path(conn, :show, user)} + %{body: body, subject_path: ~p"/profiles/#{user}"} end end diff --git a/lib/philomena_web/controllers/admin/user/vote_controller.ex b/lib/philomena_web/controllers/admin/user/vote_controller.ex index 405bcd7df..a319e3f60 100644 --- a/lib/philomena_web/controllers/admin/user/vote_controller.ex +++ b/lib/philomena_web/controllers/admin/user/vote_controller.ex @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.Admin.User.VoteController do conn |> put_flash(:alert, "Vote and fave wipe started.") - |> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user/wipe_controller.ex b/lib/philomena_web/controllers/admin/user/wipe_controller.ex index 8f194a386..1e8871666 100644 --- a/lib/philomena_web/controllers/admin/user/wipe_controller.ex +++ b/lib/philomena_web/controllers/admin/user/wipe_controller.ex @@ -15,7 +15,7 @@ defmodule PhilomenaWeb.Admin.User.WipeController do :alert, "PII wipe queued, please verify and then deactivate the account as necessary." ) - |> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/admin/user_ban_controller.ex b/lib/philomena_web/controllers/admin/user_ban_controller.ex index f98c33378..24847076f 100644 --- a/lib/philomena_web/controllers/admin/user_ban_controller.ex +++ b/lib/philomena_web/controllers/admin/user_ban_controller.ex @@ -50,8 +50,8 @@ defmodule PhilomenaWeb.Admin.UserBanController do {:ok, user_ban} -> conn |> put_flash(:info, "User was successfully banned.") - |> moderation_log(details: &log_details/3, data: user_ban) - |> redirect(to: Routes.admin_user_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: user_ban) + |> redirect(to: ~p"/admin/user_bans") {:error, :user_ban, changeset, _changes} -> render(conn, "new.html", changeset: changeset) @@ -71,8 +71,8 @@ defmodule PhilomenaWeb.Admin.UserBanController do {:ok, user_ban} -> conn |> put_flash(:info, "User ban successfully updated.") - |> moderation_log(details: &log_details/3, data: user_ban) - |> redirect(to: Routes.admin_user_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: user_ban) + |> redirect(to: ~p"/admin/user_bans") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -84,8 +84,8 @@ defmodule PhilomenaWeb.Admin.UserBanController do conn |> put_flash(:info, "User ban successfully deleted.") - |> moderation_log(details: &log_details/3, data: user_ban) - |> redirect(to: Routes.admin_user_ban_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: user_ban) + |> redirect(to: ~p"/admin/user_bans") end defp load_bans(queryable, conn) do @@ -116,7 +116,7 @@ defmodule PhilomenaWeb.Admin.UserBanController do end end - defp log_details(conn, action, ban) do + defp log_details(action, ban) do body = case action do :create -> "Created a user ban #{ban.generated_ban_id}" @@ -124,6 +124,6 @@ defmodule PhilomenaWeb.Admin.UserBanController do :delete -> "Deleted a user ban #{ban.generated_ban_id}" end - %{body: body, subject_path: Routes.admin_user_ban_path(conn, :index)} + %{body: body, subject_path: ~p"/admin/user_bans"} end end diff --git a/lib/philomena_web/controllers/admin/user_controller.ex b/lib/philomena_web/controllers/admin/user_controller.ex index fe31d2f43..266291de8 100644 --- a/lib/philomena_web/controllers/admin/user_controller.ex +++ b/lib/philomena_web/controllers/admin/user_controller.ex @@ -62,8 +62,8 @@ defmodule PhilomenaWeb.Admin.UserController do {:ok, user} -> conn |> put_flash(:info, "User successfully updated.") - |> moderation_log(details: &log_details/3, data: user) - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> moderation_log(details: &log_details/2, data: user) + |> redirect(to: ~p"/profiles/#{user}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -81,10 +81,10 @@ defmodule PhilomenaWeb.Admin.UserController do assign(conn, :roles, Repo.all(Role)) end - defp log_details(conn, _action, user) do + defp log_details(_action, user) do %{ body: "Updated user details for #{user.name}", - subject_path: Routes.profile_path(conn, :show, user) + subject_path: ~p"/profiles/#{user}" } end end diff --git a/lib/philomena_web/controllers/avatar_controller.ex b/lib/philomena_web/controllers/avatar_controller.ex index 50a30cf2b..9b6e45441 100644 --- a/lib/philomena_web/controllers/avatar_controller.ex +++ b/lib/philomena_web/controllers/avatar_controller.ex @@ -18,7 +18,7 @@ defmodule PhilomenaWeb.AvatarController do {:ok, _user} -> conn |> put_flash(:info, "Successfully updated avatar.") - |> redirect(to: Routes.avatar_path(conn, :edit)) + |> redirect(to: ~p"/avatar/edit") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -30,6 +30,6 @@ defmodule PhilomenaWeb.AvatarController do conn |> put_flash(:info, "Successfully removed avatar.") - |> redirect(to: Routes.avatar_path(conn, :edit)) + |> redirect(to: ~p"/avatar/edit") end end diff --git a/lib/philomena_web/controllers/channel/nsfw_controller.ex b/lib/philomena_web/controllers/channel/nsfw_controller.ex index f695fb4d2..311251228 100644 --- a/lib/philomena_web/controllers/channel/nsfw_controller.ex +++ b/lib/philomena_web/controllers/channel/nsfw_controller.ex @@ -7,14 +7,14 @@ defmodule PhilomenaWeb.Channel.NsfwController do conn |> set_cookie("chan_nsfw", "true") |> put_flash(:info, "Successfully updated channel visibility.") - |> redirect(to: Routes.channel_path(conn, :index)) + |> redirect(to: ~p"/channels") end def delete(conn, _params) do conn |> set_cookie("chan_nsfw", "false") |> put_flash(:info, "Successfully updated channel visibility.") - |> redirect(to: Routes.channel_path(conn, :index)) + |> redirect(to: ~p"/channels") end # Duplicated from setting controller diff --git a/lib/philomena_web/controllers/channel_controller.ex b/lib/philomena_web/controllers/channel_controller.ex index 973017d35..6d88d257e 100644 --- a/lib/philomena_web/controllers/channel_controller.ex +++ b/lib/philomena_web/controllers/channel_controller.ex @@ -52,7 +52,7 @@ defmodule PhilomenaWeb.ChannelController do {:ok, _channel} -> conn |> put_flash(:info, "Channel created successfully.") - |> redirect(to: Routes.channel_path(conn, :index)) + |> redirect(to: ~p"/channels") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -69,7 +69,7 @@ defmodule PhilomenaWeb.ChannelController do {:ok, _channel} -> conn |> put_flash(:info, "Channel updated successfully.") - |> redirect(to: Routes.channel_path(conn, :index)) + |> redirect(to: ~p"/channels") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -81,7 +81,7 @@ defmodule PhilomenaWeb.ChannelController do conn |> put_flash(:info, "Channel destroyed successfully.") - |> redirect(to: Routes.channel_path(conn, :index)) + |> redirect(to: ~p"/channels") end defp maybe_search(query, %{"cq" => cq}) when is_binary(cq) and cq != "" do diff --git a/lib/philomena_web/controllers/confirmation_controller.ex b/lib/philomena_web/controllers/confirmation_controller.ex index 37e96b18e..64b0f2b7e 100644 --- a/lib/philomena_web/controllers/confirmation_controller.ex +++ b/lib/philomena_web/controllers/confirmation_controller.ex @@ -14,7 +14,7 @@ defmodule PhilomenaWeb.ConfirmationController do if user = Users.get_user_by_email(email) do Users.deliver_user_confirmation_instructions( user, - &Routes.confirmation_url(conn, :show, &1) + &url(~p"/confirmations/#{&1}") ) end diff --git a/lib/philomena_web/controllers/conversation/hide_controller.ex b/lib/philomena_web/controllers/conversation/hide_controller.ex index 44bd8bada..71480de62 100644 --- a/lib/philomena_web/controllers/conversation/hide_controller.ex +++ b/lib/philomena_web/controllers/conversation/hide_controller.ex @@ -20,7 +20,7 @@ defmodule PhilomenaWeb.Conversation.HideController do conn |> put_flash(:info, "Conversation hidden.") - |> redirect(to: Routes.conversation_path(conn, :index)) + |> redirect(to: ~p"/conversations") end def delete(conn, _params) do @@ -31,6 +31,6 @@ defmodule PhilomenaWeb.Conversation.HideController do conn |> put_flash(:info, "Conversation restored.") - |> redirect(to: Routes.conversation_path(conn, :show, conversation)) + |> redirect(to: ~p"/conversations/#{conversation}") end end diff --git a/lib/philomena_web/controllers/conversation/message/approve_controller.ex b/lib/philomena_web/controllers/conversation/message/approve_controller.ex index 3ecade269..1693f4323 100644 --- a/lib/philomena_web/controllers/conversation/message/approve_controller.ex +++ b/lib/philomena_web/controllers/conversation/message/approve_controller.ex @@ -20,11 +20,11 @@ defmodule PhilomenaWeb.Conversation.Message.ApproveController do conn |> put_flash(:info, "Conversation message approved.") - |> moderation_log(details: &log_details/3, data: message) + |> moderation_log(details: &log_details/2, data: message) |> redirect(to: "/") end - defp log_details(_conn, _action, message) do + defp log_details(_action, message) do %{ body: "Approved private message in conversation ##{message.conversation_id}", subject_path: "/" diff --git a/lib/philomena_web/controllers/conversation/message_controller.ex b/lib/philomena_web/controllers/conversation/message_controller.ex index 7e2389023..8bd449406 100644 --- a/lib/philomena_web/controllers/conversation/message_controller.ex +++ b/lib/philomena_web/controllers/conversation/message_controller.ex @@ -36,12 +36,12 @@ defmodule PhilomenaWeb.Conversation.MessageController do conn |> put_flash(:info, "Message successfully sent.") - |> redirect(to: Routes.conversation_path(conn, :show, conversation, page: page)) + |> redirect(to: ~p"/conversations/#{conversation}?#{[page: page]}") _error -> conn |> put_flash(:error, "There was an error posting your message") - |> redirect(to: Routes.conversation_path(conn, :show, conversation)) + |> redirect(to: ~p"/conversations/#{conversation}") end end end diff --git a/lib/philomena_web/controllers/conversation/read_controller.ex b/lib/philomena_web/controllers/conversation/read_controller.ex index 93a5601e9..aa7772741 100644 --- a/lib/philomena_web/controllers/conversation/read_controller.ex +++ b/lib/philomena_web/controllers/conversation/read_controller.ex @@ -20,7 +20,7 @@ defmodule PhilomenaWeb.Conversation.ReadController do conn |> put_flash(:info, "Conversation marked as read.") - |> redirect(to: Routes.conversation_path(conn, :show, conversation)) + |> redirect(to: ~p"/conversations/#{conversation}") end def delete(conn, _params) do @@ -31,6 +31,6 @@ defmodule PhilomenaWeb.Conversation.ReadController do conn |> put_flash(:info, "Conversation marked as unread.") - |> redirect(to: Routes.conversation_path(conn, :index)) + |> redirect(to: ~p"/conversations") end end diff --git a/lib/philomena_web/controllers/conversation/report_controller.ex b/lib/philomena_web/controllers/conversation/report_controller.ex index 634bed1aa..dab814824 100644 --- a/lib/philomena_web/controllers/conversation/report_controller.ex +++ b/lib/philomena_web/controllers/conversation/report_controller.ex @@ -22,7 +22,7 @@ defmodule PhilomenaWeb.Conversation.ReportController do def new(conn, _params) do conversation = conn.assigns.conversation - action = Routes.conversation_report_path(conn, :create, conversation) + action = ~p"/conversations/#{conversation}/reports" changeset = %Report{reportable_type: "Conversation", reportable_id: conversation.id} @@ -40,7 +40,7 @@ defmodule PhilomenaWeb.Conversation.ReportController do def create(conn, params) do conversation = conn.assigns.conversation - action = Routes.conversation_report_path(conn, :create, conversation) + action = ~p"/conversations/#{conversation}/reports" ReportController.create(conn, action, conversation, "Conversation", params) end diff --git a/lib/philomena_web/controllers/conversation_controller.ex b/lib/philomena_web/controllers/conversation_controller.ex index 8b936f8e3..12784b424 100644 --- a/lib/philomena_web/controllers/conversation_controller.ex +++ b/lib/philomena_web/controllers/conversation_controller.ex @@ -115,7 +115,7 @@ defmodule PhilomenaWeb.ConversationController do conn |> put_flash(:info, "Conversation successfully created.") - |> redirect(to: Routes.conversation_path(conn, :show, conversation)) + |> redirect(to: ~p"/conversations/#{conversation}") {:error, changeset} -> conn diff --git a/lib/philomena_web/controllers/dnp_entry_controller.ex b/lib/philomena_web/controllers/dnp_entry_controller.ex index ff3f1a075..d2e675b23 100644 --- a/lib/philomena_web/controllers/dnp_entry_controller.ex +++ b/lib/philomena_web/controllers/dnp_entry_controller.ex @@ -97,7 +97,7 @@ defmodule PhilomenaWeb.DnpEntryController do {:ok, dnp_entry} -> conn |> put_flash(:info, "Successfully submitted DNP request.") - |> redirect(to: Routes.dnp_entry_path(conn, :show, dnp_entry)) + |> redirect(to: ~p"/dnp/#{dnp_entry}") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -122,7 +122,7 @@ defmodule PhilomenaWeb.DnpEntryController do {:ok, dnp_entry} -> conn |> put_flash(:info, "Successfully updated DNP request.") - |> redirect(to: Routes.dnp_entry_path(conn, :show, dnp_entry)) + |> redirect(to: ~p"/dnp/#{dnp_entry}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/duplicate_report/accept_controller.ex b/lib/philomena_web/controllers/duplicate_report/accept_controller.ex index 35c5f78fc..2d45f4578 100644 --- a/lib/philomena_web/controllers/duplicate_report/accept_controller.ex +++ b/lib/philomena_web/controllers/duplicate_report/accept_controller.ex @@ -20,21 +20,21 @@ defmodule PhilomenaWeb.DuplicateReport.AcceptController do {:ok, report} -> conn |> put_flash(:info, "Successfully accepted report.") - |> moderation_log(details: &log_details/3, data: report.duplicate_report) - |> redirect(to: Routes.duplicate_report_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: report.duplicate_report) + |> redirect(to: ~p"/duplicate_reports") _error -> conn |> put_flash(:warning, "Failed to accept report! Maybe someone else already accepted it.") - |> redirect(to: Routes.duplicate_report_path(conn, :index)) + |> redirect(to: ~p"/duplicate_reports") end end - defp log_details(conn, _action, report) do + defp log_details(_action, report) do %{ body: "Accepted duplicate report, merged #{report.image.id} into #{report.duplicate_of_image.id}", - subject_path: Routes.image_path(conn, :show, report.image) + subject_path: ~p"/images/#{report.image}" } end end diff --git a/lib/philomena_web/controllers/duplicate_report/accept_reverse_controller.ex b/lib/philomena_web/controllers/duplicate_report/accept_reverse_controller.ex index 219784780..1c7125d86 100644 --- a/lib/philomena_web/controllers/duplicate_report/accept_reverse_controller.ex +++ b/lib/philomena_web/controllers/duplicate_report/accept_reverse_controller.ex @@ -20,21 +20,21 @@ defmodule PhilomenaWeb.DuplicateReport.AcceptReverseController do {:ok, report} -> conn |> put_flash(:info, "Successfully accepted report in reverse.") - |> moderation_log(details: &log_details/3, data: report.duplicate_report) - |> redirect(to: Routes.duplicate_report_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: report.duplicate_report) + |> redirect(to: ~p"/duplicate_reports") _error -> conn |> put_flash(:warning, "Failed to accept report! Maybe someone else already accepted it.") - |> redirect(to: Routes.duplicate_report_path(conn, :index)) + |> redirect(to: ~p"/duplicate_reports") end end - defp log_details(conn, _action, report) do + defp log_details(_action, report) do %{ body: "Reverse-accepted duplicate report, merged #{report.image.id} into #{report.duplicate_of_image.id}", - subject_path: Routes.image_path(conn, :show, report.image) + subject_path: ~p"/images/#{report.image}" } end end diff --git a/lib/philomena_web/controllers/duplicate_report/claim_controller.ex b/lib/philomena_web/controllers/duplicate_report/claim_controller.ex index 31646f568..2abec4b30 100644 --- a/lib/philomena_web/controllers/duplicate_report/claim_controller.ex +++ b/lib/philomena_web/controllers/duplicate_report/claim_controller.ex @@ -20,8 +20,8 @@ defmodule PhilomenaWeb.DuplicateReport.ClaimController do conn |> put_flash(:info, "Successfully claimed report.") - |> moderation_log(details: &log_details/3, data: report) - |> redirect(to: Routes.duplicate_report_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: report) + |> redirect(to: ~p"/duplicate_reports") end def delete(conn, _params) do @@ -29,11 +29,11 @@ defmodule PhilomenaWeb.DuplicateReport.ClaimController do conn |> put_flash(:info, "Successfully released report.") - |> moderation_log(details: &log_details/3) - |> redirect(to: Routes.duplicate_report_path(conn, :index)) + |> moderation_log(details: &log_details/2) + |> redirect(to: ~p"/duplicate_reports") end - defp log_details(conn, action, _) do + defp log_details(action, _) do body = case action do :create -> "Claimed a duplicate report" @@ -42,7 +42,7 @@ defmodule PhilomenaWeb.DuplicateReport.ClaimController do %{ body: body, - subject_path: Routes.duplicate_report_path(conn, :index) + subject_path: ~p"/duplicate_reports" } end end diff --git a/lib/philomena_web/controllers/duplicate_report/reject_controller.ex b/lib/philomena_web/controllers/duplicate_report/reject_controller.ex index e019fb2a9..957a92c85 100644 --- a/lib/philomena_web/controllers/duplicate_report/reject_controller.ex +++ b/lib/philomena_web/controllers/duplicate_report/reject_controller.ex @@ -21,14 +21,14 @@ defmodule PhilomenaWeb.DuplicateReport.RejectController do conn |> put_flash(:info, "Successfully rejected report.") - |> moderation_log(details: &log_details/3, data: report) - |> redirect(to: Routes.duplicate_report_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: report) + |> redirect(to: ~p"/duplicate_reports") end - defp log_details(conn, _action, report) do + defp log_details(_action, report) do %{ body: "Rejected duplicate report (#{report.image.id} -> #{report.duplicate_of_image.id})", - subject_path: Routes.duplicate_report_path(conn, :index) + subject_path: ~p"/duplicate_reports" } end end diff --git a/lib/philomena_web/controllers/duplicate_report_controller.ex b/lib/philomena_web/controllers/duplicate_report_controller.ex index f30539a0e..5fd80e38a 100644 --- a/lib/philomena_web/controllers/duplicate_report_controller.ex +++ b/lib/philomena_web/controllers/duplicate_report_controller.ex @@ -56,12 +56,12 @@ defmodule PhilomenaWeb.DuplicateReportController do {:ok, duplicate_report} -> conn |> put_flash(:info, "Duplicate report created successfully.") - |> redirect(to: Routes.image_path(conn, :show, duplicate_report.image_id)) + |> redirect(to: ~p"/images/#{duplicate_report.image_id}") {:error, _changeset} -> conn |> put_flash(:error, "Failed to submit duplicate report") - |> redirect(to: Routes.image_path(conn, :show, source)) + |> redirect(to: ~p"/images/#{source}") end end diff --git a/lib/philomena_web/controllers/filter/clear_recent.ex b/lib/philomena_web/controllers/filter/clear_recent.ex index f3aed68d7..24f321d32 100644 --- a/lib/philomena_web/controllers/filter/clear_recent.ex +++ b/lib/philomena_web/controllers/filter/clear_recent.ex @@ -10,6 +10,6 @@ defmodule PhilomenaWeb.Filter.ClearRecentController do conn |> put_flash(:info, "Cleared recent filters.") - |> redirect(to: Routes.filter_path(conn, :index)) + |> redirect(to: ~p"/filters") end end diff --git a/lib/philomena_web/controllers/filter/public_controller.ex b/lib/philomena_web/controllers/filter/public_controller.ex index 80e23308b..e31a82194 100644 --- a/lib/philomena_web/controllers/filter/public_controller.ex +++ b/lib/philomena_web/controllers/filter/public_controller.ex @@ -12,12 +12,12 @@ defmodule PhilomenaWeb.Filter.PublicController do {:ok, filter} -> conn |> put_flash(:info, "Successfully made filter public.") - |> redirect(to: Routes.filter_path(conn, :show, filter)) + |> redirect(to: ~p"/filters/#{filter}") _error -> conn |> put_flash(:error, "Couldn't make filter public!") - |> redirect(to: Routes.filter_path(conn, :show, conn.assigns.filter)) + |> redirect(to: ~p"/filters/#{conn.assigns.filter}") end end end diff --git a/lib/philomena_web/controllers/filter_controller.ex b/lib/philomena_web/controllers/filter_controller.ex index 0d188f381..63546767e 100644 --- a/lib/philomena_web/controllers/filter_controller.ex +++ b/lib/philomena_web/controllers/filter_controller.ex @@ -146,7 +146,7 @@ defmodule PhilomenaWeb.FilterController do {:ok, filter} -> conn |> put_flash(:info, "Filter created successfully.") - |> redirect(to: Routes.filter_path(conn, :show, filter)) + |> redirect(to: ~p"/filters/#{filter}") {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) @@ -171,7 +171,7 @@ defmodule PhilomenaWeb.FilterController do {:ok, filter} -> conn |> put_flash(:info, "Filter updated successfully.") - |> redirect(to: Routes.filter_path(conn, :show, filter)) + |> redirect(to: ~p"/filters/#{filter}") {:error, %Ecto.Changeset{} = changeset} -> render(conn, "edit.html", filter: filter, changeset: changeset) @@ -185,12 +185,12 @@ defmodule PhilomenaWeb.FilterController do {:ok, _filter} -> conn |> put_flash(:info, "Filter deleted successfully.") - |> redirect(to: Routes.filter_path(conn, :index)) + |> redirect(to: ~p"/filters") _error -> conn |> put_flash(:warning, "Filter is still in use, not deleted.") - |> redirect(to: Routes.filter_path(conn, :show, filter)) + |> redirect(to: ~p"/filters/#{filter}") end end end diff --git a/lib/philomena_web/controllers/gallery/report_controller.ex b/lib/philomena_web/controllers/gallery/report_controller.ex index 879fd8fd8..3d4b5fd5e 100644 --- a/lib/philomena_web/controllers/gallery/report_controller.ex +++ b/lib/philomena_web/controllers/gallery/report_controller.ex @@ -21,7 +21,7 @@ defmodule PhilomenaWeb.Gallery.ReportController do def new(conn, _params) do gallery = conn.assigns.gallery - action = Routes.gallery_report_path(conn, :create, gallery) + action = ~p"/galleries/#{gallery}/reports" changeset = %Report{reportable_type: "Gallery", reportable_id: gallery.id} @@ -39,7 +39,7 @@ defmodule PhilomenaWeb.Gallery.ReportController do def create(conn, params) do gallery = conn.assigns.gallery - action = Routes.gallery_report_path(conn, :create, gallery) + action = ~p"/galleries/#{gallery}/reports" ReportController.create(conn, action, gallery, "Gallery", params) end diff --git a/lib/philomena_web/controllers/gallery_controller.ex b/lib/philomena_web/controllers/gallery_controller.ex index ee19d2300..2b298d2c2 100644 --- a/lib/philomena_web/controllers/gallery_controller.ex +++ b/lib/philomena_web/controllers/gallery_controller.ex @@ -110,7 +110,7 @@ defmodule PhilomenaWeb.GalleryController do {:ok, gallery} -> conn |> put_flash(:info, "Gallery successfully created.") - |> redirect(to: Routes.gallery_path(conn, :show, gallery)) + |> redirect(to: ~p"/galleries/#{gallery}") {:error, changeset} -> conn @@ -132,7 +132,7 @@ defmodule PhilomenaWeb.GalleryController do {:ok, gallery} -> conn |> put_flash(:info, "Gallery successfully updated.") - |> redirect(to: Routes.gallery_path(conn, :show, gallery)) + |> redirect(to: ~p"/galleries/#{gallery}") {:error, changeset} -> conn @@ -147,7 +147,7 @@ defmodule PhilomenaWeb.GalleryController do conn |> put_flash(:info, "Gallery successfully destroyed.") - |> redirect(to: Routes.gallery_path(conn, :index)) + |> redirect(to: ~p"/galleries") end defp prev_next_page_images(conn, query) do diff --git a/lib/philomena_web/controllers/image/anonymous_controller.ex b/lib/philomena_web/controllers/image/anonymous_controller.ex index d74b1682d..e36b5020b 100644 --- a/lib/philomena_web/controllers/image/anonymous_controller.ex +++ b/lib/philomena_web/controllers/image/anonymous_controller.ex @@ -22,8 +22,8 @@ defmodule PhilomenaWeb.Image.AnonymousController do conn |> put_flash(:info, "Successfully updated anonymity.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end defp verify_authorized(conn, _opts) do @@ -33,10 +33,10 @@ defmodule PhilomenaWeb.Image.AnonymousController do end end - defp log_details(conn, _action, image) do + defp log_details(_action, image) do %{ body: "Updated anonymity of image >>#{image.id}", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/approve_controller.ex b/lib/philomena_web/controllers/image/approve_controller.ex index 689339f82..bbb3e8da6 100644 --- a/lib/philomena_web/controllers/image/approve_controller.ex +++ b/lib/philomena_web/controllers/image/approve_controller.ex @@ -14,11 +14,11 @@ defmodule PhilomenaWeb.Image.ApproveController do conn |> put_flash(:info, "Image has been approved.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.admin_approval_path(conn, :index)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/admin/approvals") end - defp log_details(conn, _action, image) do - %{body: "Approved image #{image.id}", subject_path: Routes.image_path(conn, :show, image)} + defp log_details(_action, image) do + %{body: "Approved image #{image.id}", subject_path: ~p"/images/#{image}"} end end diff --git a/lib/philomena_web/controllers/image/comment/approve_controller.ex b/lib/philomena_web/controllers/image/comment/approve_controller.ex index 699d0cc2c..617d02806 100644 --- a/lib/philomena_web/controllers/image/comment/approve_controller.ex +++ b/lib/philomena_web/controllers/image/comment/approve_controller.ex @@ -22,14 +22,14 @@ defmodule PhilomenaWeb.Image.Comment.ApproveController do conn |> put_flash(:info, "Comment has been approved.") - |> moderation_log(details: &log_details/3, data: comment) - |> redirect(to: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}") + |> moderation_log(details: &log_details/2, data: comment) + |> redirect(to: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}") end - defp log_details(conn, _action, comment) do + defp log_details(_action, comment) do %{ body: "Approved comment on image >>#{comment.image_id}", - subject_path: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" + subject_path: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}" } end end diff --git a/lib/philomena_web/controllers/image/comment/delete_controller.ex b/lib/philomena_web/controllers/image/comment/delete_controller.ex index 4527e759f..2cf8fb39f 100644 --- a/lib/philomena_web/controllers/image/comment/delete_controller.ex +++ b/lib/philomena_web/controllers/image/comment/delete_controller.ex @@ -16,24 +16,20 @@ defmodule PhilomenaWeb.Image.Comment.DeleteController do conn |> put_flash(:info, "Comment successfully destroyed!") - |> moderation_log(details: &log_details/3, data: comment) - |> redirect( - to: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" - ) + |> moderation_log(details: &log_details/2, data: comment) + |> redirect(to: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to destroy comment!") - |> redirect( - to: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" - ) + |> redirect(to: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}") end end - defp log_details(conn, _action, comment) do + defp log_details(_action, comment) do %{ body: "Destroyed comment on image >>#{comment.image_id}", - subject_path: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" + subject_path: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}" } end end diff --git a/lib/philomena_web/controllers/image/comment/hide_controller.ex b/lib/philomena_web/controllers/image/comment/hide_controller.ex index 1e55f86db..2fe709a5b 100644 --- a/lib/philomena_web/controllers/image/comment/hide_controller.ex +++ b/lib/philomena_web/controllers/image/comment/hide_controller.ex @@ -15,17 +15,13 @@ defmodule PhilomenaWeb.Image.Comment.HideController do {:ok, comment} -> conn |> put_flash(:info, "Comment successfully hidden!") - |> moderation_log(details: &log_details/3, data: comment) - |> redirect( - to: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" - ) + |> moderation_log(details: &log_details/2, data: comment) + |> redirect(to: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}") _error -> conn |> put_flash(:error, "Unable to hide comment!") - |> redirect( - to: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" - ) + |> redirect(to: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}") end end @@ -36,21 +32,17 @@ defmodule PhilomenaWeb.Image.Comment.HideController do {:ok, comment} -> conn |> put_flash(:info, "Comment successfully unhidden!") - |> moderation_log(details: &log_details/3, data: comment) - |> redirect( - to: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" - ) + |> moderation_log(details: &log_details/2, data: comment) + |> redirect(to: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to unhide comment!") - |> redirect( - to: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" - ) + |> redirect(to: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}") end end - defp log_details(conn, action, comment) do + defp log_details(action, comment) do body = case action do :create -> "Hidden comment on image >>#{comment.image_id} (#{comment.deletion_reason})" @@ -59,7 +51,7 @@ defmodule PhilomenaWeb.Image.Comment.HideController do %{ body: body, - subject_path: Routes.image_path(conn, :show, comment.image_id) <> "#comment_#{comment.id}" + subject_path: ~p"/images/#{comment.image_id}" <> "#comment_#{comment.id}" } end end diff --git a/lib/philomena_web/controllers/image/comment/report_controller.ex b/lib/philomena_web/controllers/image/comment/report_controller.ex index 27725a4b0..d957abbd6 100644 --- a/lib/philomena_web/controllers/image/comment/report_controller.ex +++ b/lib/philomena_web/controllers/image/comment/report_controller.ex @@ -24,7 +24,7 @@ defmodule PhilomenaWeb.Image.Comment.ReportController do def new(conn, _params) do comment = conn.assigns.comment - action = Routes.image_comment_report_path(conn, :create, comment.image, comment) + action = ~p"/images/#{comment.image}/comments/#{comment}/reports" changeset = %Report{reportable_type: "Comment", reportable_id: comment.id} @@ -42,7 +42,7 @@ defmodule PhilomenaWeb.Image.Comment.ReportController do def create(conn, params) do comment = conn.assigns.comment - action = Routes.image_comment_report_path(conn, :create, comment.image, comment) + action = ~p"/images/#{comment.image}/comments/#{comment}/reports" ReportController.create(conn, action, comment, "Comment", params) end diff --git a/lib/philomena_web/controllers/image/comment_controller.ex b/lib/philomena_web/controllers/image/comment_controller.ex index cfcda7776..ca0ff9adc 100644 --- a/lib/philomena_web/controllers/image/comment_controller.ex +++ b/lib/philomena_web/controllers/image/comment_controller.ex @@ -42,7 +42,7 @@ defmodule PhilomenaWeb.Image.CommentController do def index(conn, %{"comment_id" => comment_id}) do page = CommentLoader.find_page(conn, conn.assigns.image, comment_id) - redirect(conn, to: Routes.image_comment_path(conn, :index, conn.assigns.image, page: page)) + redirect(conn, to: ~p"/images/#{conn.assigns.image}/comments?#{[page: page]}") end def index(conn, _params) do @@ -93,7 +93,7 @@ defmodule PhilomenaWeb.Image.CommentController do _error -> conn |> put_flash(:error, "There was an error posting your comment") - |> redirect(to: Routes.image_path(conn, :show, image)) + |> redirect(to: ~p"/images/#{image}") end end @@ -126,9 +126,7 @@ defmodule PhilomenaWeb.Image.CommentController do conn |> put_flash(:info, "Comment updated successfully.") - |> redirect( - to: Routes.image_path(conn, :show, conn.assigns.image) <> "#comment_#{comment.id}" - ) + |> redirect(to: ~p"/images/#{conn.assigns.image}" <> "#comment_#{comment.id}") {:error, :comment, changeset, _changes} -> render(conn, "edit.html", comment: conn.assigns.comment, changeset: changeset) diff --git a/lib/philomena_web/controllers/image/comment_lock_controller.ex b/lib/philomena_web/controllers/image/comment_lock_controller.ex index e913bdb69..432d1283b 100644 --- a/lib/philomena_web/controllers/image/comment_lock_controller.ex +++ b/lib/philomena_web/controllers/image/comment_lock_controller.ex @@ -12,8 +12,8 @@ defmodule PhilomenaWeb.Image.CommentLockController do conn |> put_flash(:info, "Successfully locked comments.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end def delete(conn, _params) do @@ -21,11 +21,11 @@ defmodule PhilomenaWeb.Image.CommentLockController do conn |> put_flash(:info, "Successfully unlocked comments.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end - defp log_details(conn, action, image) do + defp log_details(action, image) do body = case action do :create -> "Locked comments on image >>#{image.id}" @@ -34,7 +34,7 @@ defmodule PhilomenaWeb.Image.CommentLockController do %{ body: body, - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/delete_controller.ex b/lib/philomena_web/controllers/image/delete_controller.ex index adae818ce..c6b22d54f 100644 --- a/lib/philomena_web/controllers/image/delete_controller.ex +++ b/lib/philomena_web/controllers/image/delete_controller.ex @@ -19,13 +19,13 @@ defmodule PhilomenaWeb.Image.DeleteController do {:ok, result} -> conn |> put_flash(:info, "Image successfully hidden.") - |> moderation_log(details: &log_details/3, data: result.image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: result.image) + |> redirect(to: ~p"/images/#{image}") _error -> conn |> put_flash(:error, "Failed to hide image.") - |> redirect(to: Routes.image_path(conn, :show, image)) + |> redirect(to: ~p"/images/#{image}") end end @@ -36,13 +36,13 @@ defmodule PhilomenaWeb.Image.DeleteController do {:ok, image} -> conn |> put_flash(:info, "Hide reason updated.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") {:error, _changeset} -> conn |> put_flash(:error, "Couldn't update hide reason.") - |> redirect(to: Routes.image_path(conn, :show, image)) + |> redirect(to: ~p"/images/#{image}") end end @@ -54,7 +54,7 @@ defmodule PhilomenaWeb.Image.DeleteController do _false -> conn |> put_flash(:error, "Cannot change hide reason on a non-hidden image!") - |> redirect(to: Routes.image_path(conn, :show, conn.assigns.image)) + |> redirect(to: ~p"/images/#{conn.assigns.image}") |> halt() end end @@ -66,11 +66,11 @@ defmodule PhilomenaWeb.Image.DeleteController do conn |> put_flash(:info, "Image successfully unhidden.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end - defp log_details(conn, action, image) do + defp log_details(action, image) do body = case action do :create -> "Hidden image >>#{image.id} (#{image.deletion_reason})" @@ -80,7 +80,7 @@ defmodule PhilomenaWeb.Image.DeleteController do %{ body: body, - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/description_lock_controller.ex b/lib/philomena_web/controllers/image/description_lock_controller.ex index ed8c9c872..9065ff6d9 100644 --- a/lib/philomena_web/controllers/image/description_lock_controller.ex +++ b/lib/philomena_web/controllers/image/description_lock_controller.ex @@ -12,8 +12,8 @@ defmodule PhilomenaWeb.Image.DescriptionLockController do conn |> put_flash(:info, "Successfully locked description.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end def delete(conn, _params) do @@ -21,11 +21,11 @@ defmodule PhilomenaWeb.Image.DescriptionLockController do conn |> put_flash(:info, "Successfully unlocked description.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end - defp log_details(conn, action, image) do + defp log_details(action, image) do body = case action do :create -> "Locked description editing on image >>#{image.id}" @@ -34,7 +34,7 @@ defmodule PhilomenaWeb.Image.DescriptionLockController do %{ body: body, - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/destroy_controller.ex b/lib/philomena_web/controllers/image/destroy_controller.ex index fe2f3e6e0..8685bc77f 100644 --- a/lib/philomena_web/controllers/image/destroy_controller.ex +++ b/lib/philomena_web/controllers/image/destroy_controller.ex @@ -15,13 +15,13 @@ defmodule PhilomenaWeb.Image.DestroyController do {:ok, image} -> conn |> put_flash(:info, "Image contents destroyed.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") _error -> conn |> put_flash(:error, "Failed to destroy image.") - |> redirect(to: Routes.image_path(conn, :show, image)) + |> redirect(to: ~p"/images/#{image}") end end @@ -33,15 +33,15 @@ defmodule PhilomenaWeb.Image.DestroyController do _false -> conn |> put_flash(:error, "Cannot destroy a non-hidden image!") - |> redirect(to: Routes.image_path(conn, :show, conn.assigns.image)) + |> redirect(to: ~p"/images/#{conn.assigns.image}") |> halt() end end - defp log_details(conn, _action, image) do + defp log_details(_action, image) do %{ body: "Hard-deleted image >>#{image.id}", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/feature_controller.ex b/lib/philomena_web/controllers/image/feature_controller.ex index 36286c1f7..4cac3d553 100644 --- a/lib/philomena_web/controllers/image/feature_controller.ex +++ b/lib/philomena_web/controllers/image/feature_controller.ex @@ -16,8 +16,8 @@ defmodule PhilomenaWeb.Image.FeatureController do conn |> put_flash(:info, "Image marked as featured image.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end defp verify_not_deleted(conn, _opts) do @@ -25,7 +25,7 @@ defmodule PhilomenaWeb.Image.FeatureController do true -> conn |> put_flash(:warning, "Cannot feature a hidden image.") - |> redirect(to: Routes.image_path(conn, :show, conn.assigns.image)) + |> redirect(to: ~p"/images/#{conn.assigns.image}") |> halt() _false -> @@ -33,10 +33,10 @@ defmodule PhilomenaWeb.Image.FeatureController do end end - defp log_details(conn, _action, image) do + defp log_details(_action, image) do %{ body: "Featured image >>#{image.id}", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/file_controller.ex b/lib/philomena_web/controllers/image/file_controller.ex index e3bd2ab12..dc0672f01 100644 --- a/lib/philomena_web/controllers/image/file_controller.ex +++ b/lib/philomena_web/controllers/image/file_controller.ex @@ -16,12 +16,12 @@ defmodule PhilomenaWeb.Image.FileController do {:ok, image} -> conn |> put_flash(:info, "Successfully updated file.") - |> redirect(to: Routes.image_path(conn, :show, image)) + |> redirect(to: ~p"/images/#{image}") _error -> conn |> put_flash(:error, "Failed to update file!") - |> redirect(to: Routes.image_path(conn, :show, conn.assigns.image)) + |> redirect(to: ~p"/images/#{conn.assigns.image}") end end @@ -30,7 +30,7 @@ defmodule PhilomenaWeb.Image.FileController do true -> conn |> put_flash(:warning, "Cannot replace a hidden image.") - |> redirect(to: Routes.image_path(conn, :show, conn.assigns.image)) + |> redirect(to: ~p"/images/#{conn.assigns.image}") |> halt() _false -> diff --git a/lib/philomena_web/controllers/image/hash_controller.ex b/lib/philomena_web/controllers/image/hash_controller.ex index ad99b133d..ab929724b 100644 --- a/lib/philomena_web/controllers/image/hash_controller.ex +++ b/lib/philomena_web/controllers/image/hash_controller.ex @@ -12,14 +12,14 @@ defmodule PhilomenaWeb.Image.HashController do conn |> put_flash(:info, "Successfully cleared hash.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end - defp log_details(conn, _action, image) do + defp log_details(_action, image) do %{ body: "Cleared hash of image >>#{image.id}", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/navigate_controller.ex b/lib/philomena_web/controllers/image/navigate_controller.ex index ab74f0381..c0827caa5 100644 --- a/lib/philomena_web/controllers/image/navigate_controller.ex +++ b/lib/philomena_web/controllers/image/navigate_controller.ex @@ -21,11 +21,11 @@ defmodule PhilomenaWeb.Image.NavigateController do |> case do {next_image, hit} -> redirect(conn, - to: Routes.image_path(conn, :show, next_image, Keyword.put(scope, :sort, hit["sort"])) + to: ~p"/images/#{next_image}?#{Keyword.put(scope, :sort, hit["sort"])}" ) nil -> - redirect(conn, to: Routes.image_path(conn, :show, image, scope)) + redirect(conn, to: ~p"/images/#{image}?#{scope}") end end @@ -41,7 +41,7 @@ defmodule PhilomenaWeb.Image.NavigateController do page_num = page_for_offset(pagination.page_size, images.total_entries) - redirect(conn, to: Routes.search_path(conn, :index, q: "*", page: page_num)) + redirect(conn, to: ~p"/search?#{[q: "*", page: page_num]}") end defp page_for_offset(per_page, offset) do diff --git a/lib/philomena_web/controllers/image/random_controller.ex b/lib/philomena_web/controllers/image/random_controller.ex index f39d8c751..9bc293f76 100644 --- a/lib/philomena_web/controllers/image/random_controller.ex +++ b/lib/philomena_web/controllers/image/random_controller.ex @@ -20,10 +20,10 @@ defmodule PhilomenaWeb.Image.RandomController do case unwrap_random_result(search_definition) do nil -> - redirect(conn, to: Routes.image_path(conn, :index)) + redirect(conn, to: ~p"/images") random_id -> - redirect(conn, to: Routes.image_path(conn, :show, random_id, scope)) + redirect(conn, to: ~p"/images/#{random_id}?#{scope}") end end diff --git a/lib/philomena_web/controllers/image/repair_controller.ex b/lib/philomena_web/controllers/image/repair_controller.ex index 7e40835ac..972d79dff 100644 --- a/lib/philomena_web/controllers/image/repair_controller.ex +++ b/lib/philomena_web/controllers/image/repair_controller.ex @@ -13,14 +13,14 @@ defmodule PhilomenaWeb.Image.RepairController do conn |> put_flash(:info, "Repair job enqueued.") - |> moderation_log(details: &log_details/3, data: conn.assigns.image) - |> redirect(to: Routes.image_path(conn, :show, conn.assigns.image)) + |> moderation_log(details: &log_details/2, data: conn.assigns.image) + |> redirect(to: ~p"/images/#{conn.assigns.image}") end - defp log_details(conn, _action, image) do + defp log_details(_action, image) do %{ body: "Repaired image >>#{image.id}", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/report_controller.ex b/lib/philomena_web/controllers/image/report_controller.ex index 99e2bace8..6956832eb 100644 --- a/lib/philomena_web/controllers/image/report_controller.ex +++ b/lib/philomena_web/controllers/image/report_controller.ex @@ -21,7 +21,7 @@ defmodule PhilomenaWeb.Image.ReportController do def new(conn, _params) do image = conn.assigns.image - action = Routes.image_report_path(conn, :create, image) + action = ~p"/images/#{image}/reports" changeset = %Report{reportable_type: "Image", reportable_id: image.id} @@ -39,7 +39,7 @@ defmodule PhilomenaWeb.Image.ReportController do def create(conn, params) do image = conn.assigns.image - action = Routes.image_report_path(conn, :create, image) + action = ~p"/images/#{image}/reports" ReportController.create(conn, action, image, "Image", params) end diff --git a/lib/philomena_web/controllers/image/scratchpad_controller.ex b/lib/philomena_web/controllers/image/scratchpad_controller.ex index 863c0bc8b..56ffe897a 100644 --- a/lib/philomena_web/controllers/image/scratchpad_controller.ex +++ b/lib/philomena_web/controllers/image/scratchpad_controller.ex @@ -17,14 +17,14 @@ defmodule PhilomenaWeb.Image.ScratchpadController do conn |> put_flash(:info, "Successfully updated moderation notes.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end - defp log_details(conn, _action, image) do + defp log_details(_action, image) do %{ body: "Updated mod notes on image >>#{image.id} (#{image.scratchpad})", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/source_history_controller.ex b/lib/philomena_web/controllers/image/source_history_controller.ex index 24bab27fc..f82a344f4 100644 --- a/lib/philomena_web/controllers/image/source_history_controller.ex +++ b/lib/philomena_web/controllers/image/source_history_controller.ex @@ -14,14 +14,14 @@ defmodule PhilomenaWeb.Image.SourceHistoryController do conn |> put_flash(:info, "Successfully deleted source history.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end - defp log_details(conn, _action, image) do + defp log_details(_action, image) do %{ body: "Deleted source history for image >>#{image.id}", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/tag_change_controller.ex b/lib/philomena_web/controllers/image/tag_change_controller.ex index 6868ed969..fc35a7824 100644 --- a/lib/philomena_web/controllers/image/tag_change_controller.ex +++ b/lib/philomena_web/controllers/image/tag_change_controller.ex @@ -43,10 +43,10 @@ defmodule PhilomenaWeb.Image.TagChangeController do conn |> put_flash(:info, "Successfully deleted tag change from history.") |> moderation_log( - details: &log_details/3, + details: &log_details/2, data: %{image: image, details: tag_change_details(tag_change)} ) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> redirect(to: ~p"/images/#{image}") end defp added_filter(query, %{"added" => "1"}), @@ -58,10 +58,10 @@ defmodule PhilomenaWeb.Image.TagChangeController do defp added_filter(query, _params), do: query - defp log_details(conn, _action, %{image: image, details: details}) do + defp log_details(_action, %{image: image, details: details}) do %{ body: "Deleted tag change #{details} on >>#{image.id} from history", - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end diff --git a/lib/philomena_web/controllers/image/tag_lock_controller.ex b/lib/philomena_web/controllers/image/tag_lock_controller.ex index 2f71b4a3d..028cbf4cf 100644 --- a/lib/philomena_web/controllers/image/tag_lock_controller.ex +++ b/lib/philomena_web/controllers/image/tag_lock_controller.ex @@ -23,8 +23,8 @@ defmodule PhilomenaWeb.Image.TagLockController do conn |> put_flash(:info, "Successfully updated list of locked tags.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end def create(conn, _params) do @@ -32,8 +32,8 @@ defmodule PhilomenaWeb.Image.TagLockController do conn |> put_flash(:info, "Successfully locked tags.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end def delete(conn, _params) do @@ -41,11 +41,11 @@ defmodule PhilomenaWeb.Image.TagLockController do conn |> put_flash(:info, "Successfully unlocked tags.") - |> moderation_log(details: &log_details/3, data: image) - |> redirect(to: Routes.image_path(conn, :show, image)) + |> moderation_log(details: &log_details/2, data: image) + |> redirect(to: ~p"/images/#{image}") end - defp log_details(conn, action, image) do + defp log_details(action, image) do body = case action do :create -> "Locked tags on image >>#{image.id}" @@ -55,7 +55,7 @@ defmodule PhilomenaWeb.Image.TagLockController do %{ body: body, - subject_path: Routes.image_path(conn, :show, image) + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image/tamper_controller.ex b/lib/philomena_web/controllers/image/tamper_controller.ex index 8eb66309e..ede16a465 100644 --- a/lib/philomena_web/controllers/image/tamper_controller.ex +++ b/lib/philomena_web/controllers/image/tamper_controller.ex @@ -25,13 +25,13 @@ defmodule PhilomenaWeb.Image.TamperController do conn |> put_flash(:info, "Vote removed.") |> moderation_log( - details: &log_details/3, - data: %{vote: result, image: image} + details: &log_details/2, + data: %{vote: result, image: image, username: conn.assigns.user.name} ) - |> redirect(to: Routes.image_path(conn, :show, conn.assigns.image)) + |> redirect(to: ~p"/images/#{conn.assigns.image}") end - defp log_details(conn, _action, data) do + defp log_details(_action, data) do image = data.image vote_type = @@ -42,8 +42,8 @@ defmodule PhilomenaWeb.Image.TamperController do end %{ - body: "Deleted #{vote_type} by #{conn.assigns.user.name} on image >>#{data.image.id}", - subject_path: Routes.image_path(conn, :show, image) + body: "Deleted #{vote_type} by #{data.username} on image >>#{data.image.id}", + subject_path: ~p"/images/#{image}" } end end diff --git a/lib/philomena_web/controllers/image_controller.ex b/lib/philomena_web/controllers/image_controller.ex index 7d300dba9..021ee0281 100644 --- a/lib/philomena_web/controllers/image_controller.ex +++ b/lib/philomena_web/controllers/image_controller.ex @@ -128,7 +128,7 @@ defmodule PhilomenaWeb.ImageController do conn |> put_flash(:info, "Image created successfully.") - |> redirect(to: Routes.image_path(conn, :show, image)) + |> redirect(to: ~p"/images/#{image}") {:error, :image, changeset, _} -> conn @@ -208,7 +208,7 @@ defmodule PhilomenaWeb.ImageController do :alert, "The image you were looking for has been marked a duplicate of the image below" ) - |> redirect(to: Routes.image_path(conn, :show, image.duplicate_id)) + |> redirect(to: ~p"/images/#{image.duplicate_id}") |> halt() true -> diff --git a/lib/philomena_web/controllers/page_controller.ex b/lib/philomena_web/controllers/page_controller.ex index 1f8490a25..d9ebae27c 100644 --- a/lib/philomena_web/controllers/page_controller.ex +++ b/lib/philomena_web/controllers/page_controller.ex @@ -26,7 +26,7 @@ defmodule PhilomenaWeb.PageController do {:ok, %{static_page: static_page}} -> conn |> put_flash(:info, "Static page successfully created.") - |> redirect(to: Routes.page_path(conn, :show, static_page)) + |> redirect(to: ~p"/pages/#{static_page}") {:error, :static_page, changeset, _changes} -> render(conn, "new.html", changeset: changeset) @@ -47,7 +47,7 @@ defmodule PhilomenaWeb.PageController do {:ok, %{static_page: static_page}} -> conn |> put_flash(:info, "Static page successfully updated.") - |> redirect(to: Routes.page_path(conn, :show, static_page)) + |> redirect(to: ~p"/pages/#{static_page}") {:error, :static_page, changeset, _changes} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/password_controller.ex b/lib/philomena_web/controllers/password_controller.ex index f042dc12e..b40227424 100644 --- a/lib/philomena_web/controllers/password_controller.ex +++ b/lib/philomena_web/controllers/password_controller.ex @@ -16,7 +16,7 @@ defmodule PhilomenaWeb.PasswordController do if user = Users.get_user_by_email(email) do Users.deliver_user_reset_password_instructions( user, - &Routes.password_url(conn, :edit, &1) + &url(~p"/passwords/#{&1}/edit") ) end @@ -40,7 +40,7 @@ defmodule PhilomenaWeb.PasswordController do {:ok, _} -> conn |> put_flash(:info, "Password reset successfully.") - |> redirect(to: Routes.session_path(conn, :new)) + |> redirect(to: ~p"/sessions/new") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/profile/artist_link_controller.ex b/lib/philomena_web/controllers/profile/artist_link_controller.ex index 593979f1b..a7a128c3c 100644 --- a/lib/philomena_web/controllers/profile/artist_link_controller.ex +++ b/lib/philomena_web/controllers/profile/artist_link_controller.ex @@ -52,9 +52,7 @@ defmodule PhilomenaWeb.Profile.ArtistLinkController do :alert, "Link submitted! Please put '#{artist_link.verification_code}' on your linked webpage now." ) - |> redirect( - to: Routes.profile_artist_link_path(conn, :show, conn.assigns.user, artist_link) - ) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}/artist_links/#{artist_link}") {:error, %Ecto.Changeset{} = changeset} -> render(conn, "new.html", changeset: changeset) @@ -77,9 +75,7 @@ defmodule PhilomenaWeb.Profile.ArtistLinkController do {:ok, artist_link} -> conn |> put_flash(:info, "Link successfully updated.") - |> redirect( - to: Routes.profile_artist_link_path(conn, :show, conn.assigns.user, artist_link) - ) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}/artist_links/#{artist_link}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/profile/award_controller.ex b/lib/philomena_web/controllers/profile/award_controller.ex index 07df98199..4e6362366 100644 --- a/lib/philomena_web/controllers/profile/award_controller.ex +++ b/lib/philomena_web/controllers/profile/award_controller.ex @@ -23,7 +23,7 @@ defmodule PhilomenaWeb.Profile.AwardController do {:ok, _award} -> conn |> put_flash(:info, "Award successfully created.") - |> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -40,7 +40,7 @@ defmodule PhilomenaWeb.Profile.AwardController do {:ok, _award} -> conn |> put_flash(:info, "Award successfully updated.") - |> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -52,7 +52,7 @@ defmodule PhilomenaWeb.Profile.AwardController do conn |> put_flash(:info, "Award successfully destroyed. By cruel and unusual means.") - |> redirect(to: Routes.profile_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/profile/commission/item_controller.ex b/lib/philomena_web/controllers/profile/commission/item_controller.ex index 7473a4ac6..c2900eb18 100644 --- a/lib/philomena_web/controllers/profile/commission/item_controller.ex +++ b/lib/philomena_web/controllers/profile/commission/item_controller.ex @@ -47,7 +47,7 @@ defmodule PhilomenaWeb.Profile.Commission.ItemController do {:ok, _multi} -> conn |> put_flash(:info, "Item successfully created.") - |> redirect(to: Routes.profile_commission_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}/commission") {:error, changeset} -> render(conn, "new.html", user: user, commission: commission, changeset: changeset) @@ -79,7 +79,7 @@ defmodule PhilomenaWeb.Profile.Commission.ItemController do {:ok, _commission} -> conn |> put_flash(:info, "Item successfully updated.") - |> redirect(to: Routes.profile_commission_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}/commission") {:error, changeset} -> render(conn, "edit.html", @@ -100,7 +100,7 @@ defmodule PhilomenaWeb.Profile.Commission.ItemController do conn |> put_flash(:info, "Item successfully deleted.") - |> redirect(to: Routes.profile_commission_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}/commission") end defp ensure_commission(conn, _opts) do diff --git a/lib/philomena_web/controllers/profile/commission/report_controller.ex b/lib/philomena_web/controllers/profile/commission/report_controller.ex index 9c9d7ac27..0ad943efd 100644 --- a/lib/philomena_web/controllers/profile/commission/report_controller.ex +++ b/lib/philomena_web/controllers/profile/commission/report_controller.ex @@ -32,7 +32,7 @@ defmodule PhilomenaWeb.Profile.Commission.ReportController do def new(conn, _params) do user = conn.assigns.user commission = conn.assigns.user.commission - action = Routes.profile_commission_report_path(conn, :create, user) + action = ~p"/profiles/#{user}/commission/reports" changeset = %Report{reportable_type: "Commission", reportable_id: commission.id} @@ -51,7 +51,7 @@ defmodule PhilomenaWeb.Profile.Commission.ReportController do def create(conn, params) do user = conn.assigns.user commission = conn.assigns.user.commission - action = Routes.profile_commission_report_path(conn, :create, user) + action = ~p"/profiles/#{user}/commission/reports" ReportController.create(conn, action, commission, "Commission", params) end diff --git a/lib/philomena_web/controllers/profile/commission_controller.ex b/lib/philomena_web/controllers/profile/commission_controller.ex index b5bf17fac..1bd2c58ef 100644 --- a/lib/philomena_web/controllers/profile/commission_controller.ex +++ b/lib/philomena_web/controllers/profile/commission_controller.ex @@ -85,7 +85,7 @@ defmodule PhilomenaWeb.Profile.CommissionController do {:ok, _commission} -> conn |> put_flash(:info, "Commission successfully created.") - |> redirect(to: Routes.profile_commission_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}/commission") {:error, changeset} -> render(conn, "new.html", changeset: changeset) @@ -104,7 +104,7 @@ defmodule PhilomenaWeb.Profile.CommissionController do {:ok, _commission} -> conn |> put_flash(:info, "Commission successfully updated.") - |> redirect(to: Routes.profile_commission_path(conn, :show, conn.assigns.user)) + |> redirect(to: ~p"/profiles/#{conn.assigns.user}/commission") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -118,7 +118,7 @@ defmodule PhilomenaWeb.Profile.CommissionController do conn |> put_flash(:info, "Commission deleted successfully.") - |> redirect(to: Routes.commission_path(conn, :index)) + |> redirect(to: ~p"/commissions") end defp ensure_commission(conn, _opts) do @@ -156,7 +156,7 @@ defmodule PhilomenaWeb.Profile.CommissionController do :warning, "You must have a verified artist link to create a commission listing." ) - |> redirect(to: Routes.commission_path(conn, :index)) + |> redirect(to: ~p"/commissions") |> halt() end end diff --git a/lib/philomena_web/controllers/profile/description_controller.ex b/lib/philomena_web/controllers/profile/description_controller.ex index 4a7836ba7..6dac8e889 100644 --- a/lib/philomena_web/controllers/profile/description_controller.ex +++ b/lib/philomena_web/controllers/profile/description_controller.ex @@ -30,7 +30,7 @@ defmodule PhilomenaWeb.Profile.DescriptionController do {:ok, _user} -> conn |> put_flash(:info, "Description successfully updated.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/profile/report_controller.ex b/lib/philomena_web/controllers/profile/report_controller.ex index 0c326b8da..80a688956 100644 --- a/lib/philomena_web/controllers/profile/report_controller.ex +++ b/lib/philomena_web/controllers/profile/report_controller.ex @@ -21,7 +21,7 @@ defmodule PhilomenaWeb.Profile.ReportController do def new(conn, _params) do user = conn.assigns.user - action = Routes.profile_report_path(conn, :create, user) + action = ~p"/profiles/#{user}/reports" changeset = %Report{reportable_type: "User", reportable_id: user.id} @@ -39,7 +39,7 @@ defmodule PhilomenaWeb.Profile.ReportController do def create(conn, params) do user = conn.assigns.user - action = Routes.profile_report_path(conn, :create, user) + action = ~p"/profiles/#{user}/reports" ReportController.create(conn, action, user, "User", params) end diff --git a/lib/philomena_web/controllers/profile/scratchpad_controller.ex b/lib/philomena_web/controllers/profile/scratchpad_controller.ex index 117fb3b6e..3132436ec 100644 --- a/lib/philomena_web/controllers/profile/scratchpad_controller.ex +++ b/lib/philomena_web/controllers/profile/scratchpad_controller.ex @@ -25,7 +25,7 @@ defmodule PhilomenaWeb.Profile.ScratchpadController do {:ok, _user} -> conn |> put_flash(:info, "Moderation scratchpad successfully updated.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/registration/email_controller.ex b/lib/philomena_web/controllers/registration/email_controller.ex index 1125ce66f..04dd14ce2 100644 --- a/lib/philomena_web/controllers/registration/email_controller.ex +++ b/lib/philomena_web/controllers/registration/email_controller.ex @@ -11,7 +11,7 @@ defmodule PhilomenaWeb.Registration.EmailController do Users.deliver_update_email_instructions( applied_user, user.email, - &Routes.registration_email_url(conn, :show, &1) + &url(~p"/registrations/email/#{&1}") ) conn @@ -19,12 +19,12 @@ defmodule PhilomenaWeb.Registration.EmailController do :alert, "A link to confirm your email change has been sent to the new address." ) - |> redirect(to: Routes.registration_path(conn, :edit)) + |> redirect(to: ~p"/registrations/edit") {:error, _changeset} -> conn |> put_flash(:error, "Failed to update email.") - |> redirect(to: Routes.registration_path(conn, :edit)) + |> redirect(to: ~p"/registrations/edit") end end @@ -33,12 +33,12 @@ defmodule PhilomenaWeb.Registration.EmailController do :ok -> conn |> put_flash(:info, "Email changed successfully.") - |> redirect(to: Routes.registration_path(conn, :edit)) + |> redirect(to: ~p"/registrations/edit") :error -> conn |> put_flash(:warning, "Email change link is invalid or it has expired.") - |> redirect(to: Routes.registration_path(conn, :edit)) + |> redirect(to: ~p"/registrations/edit") end end end diff --git a/lib/philomena_web/controllers/registration/name_controller.ex b/lib/philomena_web/controllers/registration/name_controller.ex index 7b5cd0dda..e0023f892 100644 --- a/lib/philomena_web/controllers/registration/name_controller.ex +++ b/lib/philomena_web/controllers/registration/name_controller.ex @@ -17,7 +17,7 @@ defmodule PhilomenaWeb.Registration.NameController do {:ok, user} -> conn |> put_flash(:info, "Name successfully updated.") - |> redirect(to: Routes.profile_path(conn, :show, user)) + |> redirect(to: ~p"/profiles/#{user}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/registration/password_controller.ex b/lib/philomena_web/controllers/registration/password_controller.ex index 909df6ebe..6e3ba04a8 100644 --- a/lib/philomena_web/controllers/registration/password_controller.ex +++ b/lib/philomena_web/controllers/registration/password_controller.ex @@ -13,13 +13,13 @@ defmodule PhilomenaWeb.Registration.PasswordController do {:ok, user} -> conn |> put_flash(:info, "Password updated successfully.") - |> put_session(:user_return_to, Routes.registration_path(conn, :edit)) + |> put_session(:user_return_to, ~p"/registrations/edit") |> UserAuth.log_in_user(user) {:error, _changeset} -> conn |> put_flash(:error, "Failed to update password.") - |> redirect(to: Routes.registration_path(conn, :edit)) + |> redirect(to: ~p"/registrations/edit") end end end diff --git a/lib/philomena_web/controllers/registration/totp_controller.ex b/lib/philomena_web/controllers/registration/totp_controller.ex index 3cea3279e..9316aeb26 100644 --- a/lib/philomena_web/controllers/registration/totp_controller.ex +++ b/lib/philomena_web/controllers/registration/totp_controller.ex @@ -16,7 +16,7 @@ defmodule PhilomenaWeb.Registration.TotpController do |> Repo.update() # Redirect to have the conn pick up the changes - redirect(conn, to: Routes.registration_totp_path(conn, :edit)) + redirect(conn, to: ~p"/registrations/totp/edit") _ -> changeset = Users.change_user(user) @@ -48,7 +48,7 @@ defmodule PhilomenaWeb.Registration.TotpController do {:ok, user} -> conn |> put_flash(:totp_backup_codes, backup_codes) - |> put_session(:user_return_to, Routes.registration_totp_path(conn, :edit)) + |> put_session(:user_return_to, ~p"/registrations/totp/edit") |> UserAuth.totp_auth_user(user, %{}) end end diff --git a/lib/philomena_web/controllers/registration_controller.ex b/lib/philomena_web/controllers/registration_controller.ex index 389ae579f..8f3e2bbd2 100644 --- a/lib/philomena_web/controllers/registration_controller.ex +++ b/lib/philomena_web/controllers/registration_controller.ex @@ -20,7 +20,7 @@ defmodule PhilomenaWeb.RegistrationController do {:ok, _} = Users.deliver_user_confirmation_instructions( user, - &Routes.confirmation_url(conn, :show, &1) + &url(~p"/confirmations/#{&1}") ) conn diff --git a/lib/philomena_web/controllers/report_controller.ex b/lib/philomena_web/controllers/report_controller.ex index 49a5459cb..c3e9ff8ca 100644 --- a/lib/philomena_web/controllers/report_controller.ex +++ b/lib/philomena_web/controllers/report_controller.ex @@ -53,7 +53,7 @@ defmodule PhilomenaWeb.ReportController do :alert, "Your report has been received and will be checked by staff shortly." ) - |> redirect(to: redirect_path(conn, conn.assigns.current_user)) + |> redirect(to: redirect_path(conn.assigns.current_user)) {:error, changeset} -> # Note that we are depending on the controller that called @@ -100,8 +100,8 @@ defmodule PhilomenaWeb.ReportController do reports_open >= max_reports() end - defp redirect_path(_conn, nil), do: "/" - defp redirect_path(conn, _user), do: Routes.report_path(conn, :index) + defp redirect_path(nil), do: "/" + defp redirect_path(_user), do: ~p"/reports" defp max_reports do 5 diff --git a/lib/philomena_web/controllers/session_controller.ex b/lib/philomena_web/controllers/session_controller.ex index ee15bc124..b5704501c 100644 --- a/lib/philomena_web/controllers/session_controller.ex +++ b/lib/philomena_web/controllers/session_controller.ex @@ -15,7 +15,7 @@ defmodule PhilomenaWeb.SessionController do Users.get_user_by_email_and_password( email, password, - &Routes.unlock_url(conn, :show, &1) + &url(~p"/unlocks/#{&1}") ) cond do diff --git a/lib/philomena_web/controllers/setting_controller.ex b/lib/philomena_web/controllers/setting_controller.ex index 68cf76b97..371f18532 100644 --- a/lib/philomena_web/controllers/setting_controller.ex +++ b/lib/philomena_web/controllers/setting_controller.ex @@ -25,7 +25,7 @@ defmodule PhilomenaWeb.SettingController do {:ok, conn} -> conn |> put_flash(:info, "Settings updated successfully.") - |> redirect(to: Routes.setting_path(conn, :edit)) + |> redirect(to: ~p"/settings/edit") {:error, changeset} -> conn diff --git a/lib/philomena_web/controllers/tag/alias_controller.ex b/lib/philomena_web/controllers/tag/alias_controller.ex index b7687b1c1..b0ed34838 100644 --- a/lib/philomena_web/controllers/tag/alias_controller.ex +++ b/lib/philomena_web/controllers/tag/alias_controller.ex @@ -23,7 +23,7 @@ defmodule PhilomenaWeb.Tag.AliasController do {:ok, tag} -> conn |> put_flash(:info, "Tag alias queued.") - |> redirect(to: Routes.tag_alias_path(conn, :edit, tag)) + |> redirect(to: ~p"/tags/#{tag}/alias/edit") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -35,6 +35,6 @@ defmodule PhilomenaWeb.Tag.AliasController do conn |> put_flash(:info, "Tag dealias queued.") - |> redirect(to: Routes.tag_path(conn, :show, tag)) + |> redirect(to: ~p"/tags/#{tag}") end end diff --git a/lib/philomena_web/controllers/tag/image_controller.ex b/lib/philomena_web/controllers/tag/image_controller.ex index 2f43ebe82..b98e1909b 100644 --- a/lib/philomena_web/controllers/tag/image_controller.ex +++ b/lib/philomena_web/controllers/tag/image_controller.ex @@ -23,8 +23,8 @@ defmodule PhilomenaWeb.Tag.ImageController do {:ok, tag} -> conn |> put_flash(:info, "Tag image successfully updated.") - |> moderation_log(details: &log_details/3, data: tag) - |> redirect(to: Routes.tag_path(conn, :show, tag)) + |> moderation_log(details: &log_details/2, data: tag) + |> redirect(to: ~p"/tags/#{tag}") {:error, :tag, changeset, _changes} -> render(conn, "edit.html", changeset: changeset) @@ -36,11 +36,11 @@ defmodule PhilomenaWeb.Tag.ImageController do conn |> put_flash(:info, "Tag image successfully removed.") - |> moderation_log(details: &log_details/3, data: tag) - |> redirect(to: Routes.tag_path(conn, :show, conn.assigns.tag)) + |> moderation_log(details: &log_details/2, data: tag) + |> redirect(to: ~p"/tags/#{conn.assigns.tag}") end - defp log_details(conn, action, tag) do + defp log_details(action, tag) do body = case action do :update -> "Updated image on tag '#{tag.name}'" @@ -49,7 +49,7 @@ defmodule PhilomenaWeb.Tag.ImageController do %{ body: body, - subject_path: Routes.tag_path(conn, :show, tag) + subject_path: ~p"/tags/#{tag}" } end end diff --git a/lib/philomena_web/controllers/tag/reindex_controller.ex b/lib/philomena_web/controllers/tag/reindex_controller.ex index 70b7163c5..aee27c282 100644 --- a/lib/philomena_web/controllers/tag/reindex_controller.ex +++ b/lib/philomena_web/controllers/tag/reindex_controller.ex @@ -18,6 +18,6 @@ defmodule PhilomenaWeb.Tag.ReindexController do conn |> put_flash(:info, "Tag reindex started.") - |> redirect(to: Routes.tag_path(conn, :edit, tag)) + |> redirect(to: ~p"/tags/#{tag}/edit") end end diff --git a/lib/philomena_web/controllers/tag_controller.ex b/lib/philomena_web/controllers/tag_controller.ex index f9abeeb06..5927c68fb 100644 --- a/lib/philomena_web/controllers/tag_controller.ex +++ b/lib/philomena_web/controllers/tag_controller.ex @@ -97,8 +97,8 @@ defmodule PhilomenaWeb.TagController do {:ok, tag} -> conn |> put_flash(:info, "Tag successfully updated.") - |> moderation_log(details: &log_details/3, data: tag) - |> redirect(to: Routes.tag_path(conn, :show, tag)) + |> moderation_log(details: &log_details/2, data: tag) + |> redirect(to: ~p"/tags/#{tag}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) @@ -110,7 +110,7 @@ defmodule PhilomenaWeb.TagController do conn |> put_flash(:info, "Tag queued for deletion.") - |> moderation_log(details: &log_details/3, data: tag) + |> moderation_log(details: &log_details/2, data: tag) |> redirect(to: "/") end @@ -167,12 +167,12 @@ defmodule PhilomenaWeb.TagController do :alert, "This tag (\"#{conn.assigns.tag.name}\") has been aliased into the tag \"#{tag.name}\"." ) - |> redirect(to: Routes.tag_path(conn, :show, tag)) + |> redirect(to: ~p"/tags/#{tag}") |> halt() end end - defp log_details(conn, action, tag) do + defp log_details(action, tag) do body = case action do :update -> "Updated details on tag '#{tag.name}'" @@ -181,7 +181,7 @@ defmodule PhilomenaWeb.TagController do %{ body: body, - subject_path: Routes.tag_path(conn, :show, tag) + subject_path: ~p"/tags/#{tag}" } end end diff --git a/lib/philomena_web/controllers/topic/hide_controller.ex b/lib/philomena_web/controllers/topic/hide_controller.ex index 3149501d9..4558f393b 100644 --- a/lib/philomena_web/controllers/topic/hide_controller.ex +++ b/lib/philomena_web/controllers/topic/hide_controller.ex @@ -27,13 +27,13 @@ defmodule PhilomenaWeb.Topic.HideController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully hidden!") - |> moderation_log(details: &log_details/3, data: topic) - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> moderation_log(details: &log_details/2, data: topic) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to hide the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end end @@ -44,17 +44,17 @@ defmodule PhilomenaWeb.Topic.HideController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully restored!") - |> moderation_log(details: &log_details/3, data: topic) - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> moderation_log(details: &log_details/2, data: topic) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to restore the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end end - defp log_details(conn, action, topic) do + defp log_details(action, topic) do body = case action do :create -> @@ -66,7 +66,7 @@ defmodule PhilomenaWeb.Topic.HideController do %{ body: body, - subject_path: Routes.forum_topic_path(conn, :show, topic.forum, topic) + subject_path: ~p"/forums/#{topic.forum}/topics/#{topic}" } end end diff --git a/lib/philomena_web/controllers/topic/lock_controller.ex b/lib/philomena_web/controllers/topic/lock_controller.ex index 88bf115ba..c9b9c21f9 100644 --- a/lib/philomena_web/controllers/topic/lock_controller.ex +++ b/lib/philomena_web/controllers/topic/lock_controller.ex @@ -26,13 +26,13 @@ defmodule PhilomenaWeb.Topic.LockController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully locked!") - |> moderation_log(details: &log_details/3, data: topic) - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> moderation_log(details: &log_details/2, data: topic) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to lock the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end end @@ -43,17 +43,17 @@ defmodule PhilomenaWeb.Topic.LockController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully unlocked!") - |> moderation_log(details: &log_details/3, data: topic) - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> moderation_log(details: &log_details/2, data: topic) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to unlock the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end end - defp log_details(conn, action, topic) do + defp log_details(action, topic) do body = case action do :create -> "Locked topic '#{topic.title}' (#{topic.lock_reason}) in #{topic.forum.name}" @@ -62,7 +62,7 @@ defmodule PhilomenaWeb.Topic.LockController do %{ body: body, - subject_path: Routes.forum_topic_path(conn, :show, topic.forum, topic) + subject_path: ~p"/forums/#{topic.forum}/topics/#{topic}" } end end diff --git a/lib/philomena_web/controllers/topic/move_controller.ex b/lib/philomena_web/controllers/topic/move_controller.ex index f12af72ec..00fe220b0 100644 --- a/lib/philomena_web/controllers/topic/move_controller.ex +++ b/lib/philomena_web/controllers/topic/move_controller.ex @@ -29,20 +29,20 @@ defmodule PhilomenaWeb.Topic.MoveController do conn |> put_flash(:info, "Topic successfully moved!") - |> moderation_log(details: &log_details/3, data: topic) - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> moderation_log(details: &log_details/2, data: topic) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to move the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, conn.assigns.forum, topic)) + |> redirect(to: ~p"/forums/#{conn.assigns.forum}/topics/#{topic}") end end - defp log_details(conn, _action, topic) do + defp log_details(_action, topic) do %{ body: "Topic '#{topic.title}' moved to #{topic.forum.name}", - subject_path: Routes.forum_topic_path(conn, :show, topic.forum, topic) + subject_path: ~p"/forums/#{topic.forum}/topics/#{topic}" } end end diff --git a/lib/philomena_web/controllers/topic/poll/vote_controller.ex b/lib/philomena_web/controllers/topic/poll/vote_controller.ex index ba4e266d6..089c5a17b 100644 --- a/lib/philomena_web/controllers/topic/poll/vote_controller.ex +++ b/lib/philomena_web/controllers/topic/poll/vote_controller.ex @@ -42,12 +42,12 @@ defmodule PhilomenaWeb.Topic.Poll.VoteController do {:ok, _votes} -> conn |> put_flash(:info, "Your vote has been recorded.") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") _error -> conn |> put_flash(:warning, "Your vote was not recorded.") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end end @@ -56,7 +56,7 @@ defmodule PhilomenaWeb.Topic.Poll.VoteController do conn |> put_flash(:error, "Your vote was not recorded.") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end def delete(conn, %{"id" => poll_vote_id}) do @@ -67,7 +67,7 @@ defmodule PhilomenaWeb.Topic.Poll.VoteController do conn |> put_flash(:info, "Vote successfully removed.") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end defp verify_authorized(conn, _opts) do diff --git a/lib/philomena_web/controllers/topic/poll_controller.ex b/lib/philomena_web/controllers/topic/poll_controller.ex index 61cec7d98..b2fdbb81c 100644 --- a/lib/philomena_web/controllers/topic/poll_controller.ex +++ b/lib/philomena_web/controllers/topic/poll_controller.ex @@ -27,9 +27,7 @@ defmodule PhilomenaWeb.Topic.PollController do {:ok, _poll} -> conn |> put_flash(:info, "Poll successfully updated.") - |> redirect( - to: Routes.forum_topic_path(conn, :show, conn.assigns.forum, conn.assigns.topic) - ) + |> redirect(to: ~p"/forums/#{conn.assigns.forum}/topics/#{conn.assigns.topic}") {:error, changeset} -> render(conn, "edit.html", changeset: changeset) diff --git a/lib/philomena_web/controllers/topic/post/approve_controller.ex b/lib/philomena_web/controllers/topic/post/approve_controller.ex index 90bf39481..65d289cac 100644 --- a/lib/philomena_web/controllers/topic/post/approve_controller.ex +++ b/lib/philomena_web/controllers/topic/post/approve_controller.ex @@ -20,10 +20,10 @@ defmodule PhilomenaWeb.Topic.Post.ApproveController do {:ok, post} -> conn |> put_flash(:info, "Post successfully approved.") - |> moderation_log(details: &log_details/3, data: post) + |> moderation_log(details: &log_details/2, data: post) |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) @@ -32,17 +32,17 @@ defmodule PhilomenaWeb.Topic.Post.ApproveController do |> put_flash(:error, "Unable to approve post!") |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) end end - defp log_details(conn, _action, post) do + defp log_details(_action, post) do %{ body: "Approved forum post ##{post.id} in topic '#{post.topic.title}'", subject_path: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" } end diff --git a/lib/philomena_web/controllers/topic/post/delete_controller.ex b/lib/philomena_web/controllers/topic/post/delete_controller.ex index 9e6d7a458..28aad707a 100644 --- a/lib/philomena_web/controllers/topic/post/delete_controller.ex +++ b/lib/philomena_web/controllers/topic/post/delete_controller.ex @@ -19,10 +19,10 @@ defmodule PhilomenaWeb.Topic.Post.DeleteController do {:ok, post} -> conn |> put_flash(:info, "Post successfully destroyed!") - |> moderation_log(details: &log_details/3, data: post) + |> moderation_log(details: &log_details/2, data: post) |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) @@ -31,17 +31,17 @@ defmodule PhilomenaWeb.Topic.Post.DeleteController do |> put_flash(:error, "Unable to destroy post!") |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) end end - defp log_details(conn, _action, post) do + defp log_details(_action, post) do %{ body: "Destroyed forum post ##{post.id} in topic '#{post.topic.title}'", subject_path: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" } end diff --git a/lib/philomena_web/controllers/topic/post/hide_controller.ex b/lib/philomena_web/controllers/topic/post/hide_controller.ex index 9e4499772..882321af3 100644 --- a/lib/philomena_web/controllers/topic/post/hide_controller.ex +++ b/lib/philomena_web/controllers/topic/post/hide_controller.ex @@ -20,10 +20,10 @@ defmodule PhilomenaWeb.Topic.Post.HideController do {:ok, post} -> conn |> put_flash(:info, "Post successfully hidden.") - |> moderation_log(details: &log_details/3, data: post) + |> moderation_log(details: &log_details/2, data: post) |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) @@ -32,7 +32,7 @@ defmodule PhilomenaWeb.Topic.Post.HideController do |> put_flash(:error, "Unable to hide post!") |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) end @@ -45,10 +45,10 @@ defmodule PhilomenaWeb.Topic.Post.HideController do {:ok, post} -> conn |> put_flash(:info, "Post successfully unhidden.") - |> moderation_log(details: &log_details/3, data: post) + |> moderation_log(details: &log_details/2, data: post) |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) @@ -57,13 +57,13 @@ defmodule PhilomenaWeb.Topic.Post.HideController do |> put_flash(:error, "Unable to unhide post!") |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) end end - defp log_details(conn, action, post) do + defp log_details(action, post) do body = case action do :create -> @@ -76,7 +76,7 @@ defmodule PhilomenaWeb.Topic.Post.HideController do %{ body: body, subject_path: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" } end diff --git a/lib/philomena_web/controllers/topic/post/report_controller.ex b/lib/philomena_web/controllers/topic/post/report_controller.ex index 2d29f7b80..f09df5117 100644 --- a/lib/philomena_web/controllers/topic/post/report_controller.ex +++ b/lib/philomena_web/controllers/topic/post/report_controller.ex @@ -26,7 +26,7 @@ defmodule PhilomenaWeb.Topic.Post.ReportController do def new(conn, _params) do topic = conn.assigns.topic post = conn.assigns.post - action = Routes.forum_topic_post_report_path(conn, :create, topic.forum, topic, post) + action = ~p"/forums/#{topic.forum}/topics/#{topic}/posts/#{post}/reports" changeset = %Report{reportable_type: "Post", reportable_id: post.id} @@ -40,7 +40,7 @@ defmodule PhilomenaWeb.Topic.Post.ReportController do def create(conn, params) do topic = conn.assigns.topic post = conn.assigns.post - action = Routes.forum_topic_post_report_path(conn, :create, topic.forum, topic, post) + action = ~p"/forums/#{topic.forum}/topics/#{topic}/posts/#{post}/reports" ReportController.create(conn, action, post, "Post", params) end diff --git a/lib/philomena_web/controllers/topic/post_controller.ex b/lib/philomena_web/controllers/topic/post_controller.ex index dce67f4ab..7513f930d 100644 --- a/lib/philomena_web/controllers/topic/post_controller.ex +++ b/lib/philomena_web/controllers/topic/post_controller.ex @@ -58,14 +58,14 @@ defmodule PhilomenaWeb.Topic.PostController do |> put_flash(:info, "Post created successfully.") |> redirect( to: - Routes.forum_topic_path(conn, :show, forum, topic, post_id: post.id) <> + ~p"/forums/#{forum}/topics/#{topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) _error -> conn |> put_flash(:warning, "There was an error creating the post") - |> redirect(to: Routes.forum_topic_path(conn, :show, forum, topic)) + |> redirect(to: ~p"/forums/#{forum}/topics/#{topic}") end end @@ -88,7 +88,7 @@ defmodule PhilomenaWeb.Topic.PostController do |> put_flash(:info, "Post successfully edited.") |> redirect( to: - Routes.forum_topic_path(conn, :show, post.topic.forum, post.topic, post_id: post.id) <> + ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" ) diff --git a/lib/philomena_web/controllers/topic/stick_controller.ex b/lib/philomena_web/controllers/topic/stick_controller.ex index 27f921686..39f0d562f 100644 --- a/lib/philomena_web/controllers/topic/stick_controller.ex +++ b/lib/philomena_web/controllers/topic/stick_controller.ex @@ -25,13 +25,13 @@ defmodule PhilomenaWeb.Topic.StickController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully stickied!") - |> moderation_log(details: &log_details/3, data: topic) - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> moderation_log(details: &log_details/2, data: topic) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to stick the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end end @@ -42,17 +42,17 @@ defmodule PhilomenaWeb.Topic.StickController do {:ok, topic} -> conn |> put_flash(:info, "Topic successfully unstickied!") - |> moderation_log(details: &log_details/3, data: topic) - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> moderation_log(details: &log_details/2, data: topic) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "Unable to unstick the topic!") - |> redirect(to: Routes.forum_topic_path(conn, :show, topic.forum, topic)) + |> redirect(to: ~p"/forums/#{topic.forum}/topics/#{topic}") end end - defp log_details(conn, action, topic) do + defp log_details(action, topic) do body = case action do :create -> "Stickied topic '#{topic.title}' in #{topic.forum.name}" @@ -61,7 +61,7 @@ defmodule PhilomenaWeb.Topic.StickController do %{ body: body, - subject_path: Routes.forum_topic_path(conn, :show, topic.forum, topic) + subject_path: ~p"/forums/#{topic.forum}/topics/#{topic}" } end end diff --git a/lib/philomena_web/controllers/topic_controller.ex b/lib/philomena_web/controllers/topic_controller.ex index 4c433be2e..e88670a0e 100644 --- a/lib/philomena_web/controllers/topic_controller.ex +++ b/lib/philomena_web/controllers/topic_controller.ex @@ -128,7 +128,7 @@ defmodule PhilomenaWeb.TopicController do conn |> put_flash(:info, "Successfully posted topic.") - |> redirect(to: Routes.forum_topic_path(conn, :show, forum, topic)) + |> redirect(to: ~p"/forums/#{forum}/topics/#{topic}") {:error, :topic, changeset, _} -> conn @@ -137,7 +137,7 @@ defmodule PhilomenaWeb.TopicController do _error -> conn |> put_flash(:error, "There was an error with your submission. Please try again.") - |> redirect(to: Routes.forum_topic_path(conn, :new, forum)) + |> redirect(to: ~p"/forums/#{forum}/topics/new") end end @@ -146,14 +146,12 @@ defmodule PhilomenaWeb.TopicController do {:ok, topic} -> conn |> put_flash(:info, "Successfully updated topic.") - |> redirect(to: Routes.forum_topic_path(conn, :show, conn.assigns.forum, topic)) + |> redirect(to: ~p"/forums/#{conn.assigns.forum}/topics/#{topic}") {:error, _changeset} -> conn |> put_flash(:error, "There was an error with your submission. Please try again.") - |> redirect( - to: Routes.forum_topic_path(conn, :show, conn.assigns.forum, conn.assigns.topic) - ) + |> redirect(to: ~p"/forums/#{conn.assigns.forum}/topics/#{conn.assigns.topic}") end end diff --git a/lib/philomena_web/controllers/unlock_controller.ex b/lib/philomena_web/controllers/unlock_controller.ex index e2e5257fd..0f5412c22 100644 --- a/lib/philomena_web/controllers/unlock_controller.ex +++ b/lib/philomena_web/controllers/unlock_controller.ex @@ -14,7 +14,7 @@ defmodule PhilomenaWeb.UnlockController do if user = Users.get_user_by_email(email) do Users.deliver_user_unlock_instructions( user, - &Routes.unlock_url(conn, :show, &1) + &url(~p"/unlocks/#{&1}") ) end diff --git a/lib/philomena_web/plugs/map_parameter_plug.ex b/lib/philomena_web/plugs/map_parameter_plug.ex index 93b7cce83..36fc7c049 100644 --- a/lib/philomena_web/plugs/map_parameter_plug.ex +++ b/lib/philomena_web/plugs/map_parameter_plug.ex @@ -3,7 +3,7 @@ defmodule PhilomenaWeb.MapParameterPlug do # symphony of failure: # # 1.) Router helpers do not strip nil query parameters. - # iex> Routes.gallery_path(conn, :index, gallery: nil) + # iex> ~p"/galleries?#{[gallery: nil]}" # "/galleries?gallery=" # # 2.) Pagination always sets the parameter in the route in order diff --git a/lib/philomena_web/plugs/moderation_log_plug.ex b/lib/philomena_web/plugs/moderation_log_plug.ex index 4ee75938d..c29f45a5a 100644 --- a/lib/philomena_web/plugs/moderation_log_plug.ex +++ b/lib/philomena_web/plugs/moderation_log_plug.ex @@ -29,7 +29,7 @@ defmodule PhilomenaWeb.ModerationLogPlug do user = conn.assigns.current_user action = Controller.action_name(conn) - %{subject_path: subject_path, body: body} = details_func.(conn, action, userdata) + %{subject_path: subject_path, body: body} = details_func.(action, userdata) mod = Controller.controller_module(conn) [mod_name] = Regex.run(@controller_regex, to_string(mod), capture: :all_but_first) diff --git a/lib/philomena_web/plugs/tor_plug.ex b/lib/philomena_web/plugs/tor_plug.ex index 8e6cc4e35..57d5745ad 100644 --- a/lib/philomena_web/plugs/tor_plug.ex +++ b/lib/philomena_web/plugs/tor_plug.ex @@ -6,7 +6,6 @@ defmodule PhilomenaWeb.TorPlug do plug PhilomenaWeb.TorPlug """ - alias PhilomenaWeb.Router.Helpers, as: Routes use PhilomenaWeb, :verified_routes alias Phoenix.Controller @@ -27,7 +26,7 @@ defmodule PhilomenaWeb.TorPlug do def maybe_redirect(conn, nil, true) do conn - |> Controller.redirect(to: Routes.session_path(conn, :new)) + |> Controller.redirect(to: ~p"/sessions/new") |> Conn.halt() end diff --git a/lib/philomena_web/plugs/totp_plug.ex b/lib/philomena_web/plugs/totp_plug.ex index 30f87378b..7f5e2d11b 100644 --- a/lib/philomena_web/plugs/totp_plug.ex +++ b/lib/philomena_web/plugs/totp_plug.ex @@ -7,7 +7,6 @@ defmodule PhilomenaWeb.TotpPlug do plug PhilomenaWeb.TotpPlug """ - alias PhilomenaWeb.Router.Helpers, as: Routes use PhilomenaWeb, :verified_routes @doc false @@ -33,7 +32,7 @@ defmodule PhilomenaWeb.TotpPlug do _falsy -> conn - |> Phoenix.Controller.redirect(to: Routes.session_totp_path(conn, :new)) + |> Phoenix.Controller.redirect(to: ~p"/sessions/totp/new") |> Plug.Conn.halt() end end diff --git a/lib/philomena_web/templates/activity/_channel_strip.html.slime b/lib/philomena_web/templates/activity/_channel_strip.html.slime index 34acd120e..278cc6e99 100644 --- a/lib/philomena_web/templates/activity/_channel_strip.html.slime +++ b/lib/philomena_web/templates/activity/_channel_strip.html.slime @@ -1,7 +1,7 @@ .block__content.flex.alternating-color .flex__grow / TODO - a href=Routes.channel_path(@conn, :show, @channel) + a href=~p"/channels/#{@channel}" /- if channel.channel_image.present? / => image_tag channel.uploaded_channel_image.url, width: 32, height: 32, alt: "#{channel.title}'s logo'", class: 'channel-strip__image' /- else diff --git a/lib/philomena_web/templates/activity/_topic_strip.html.slime b/lib/philomena_web/templates/activity/_topic_strip.html.slime index 4a18e7fbb..37b4f543b 100644 --- a/lib/philomena_web/templates/activity/_topic_strip.html.slime +++ b/lib/philomena_web/templates/activity/_topic_strip.html.slime @@ -4,7 +4,7 @@ = if @topic.last_post do span.hyphenate-breaks = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @topic.last_post, conn: @conn - = link("replied to", to: Routes.forum_topic_path(@conn, :show, @topic.forum, @topic, post_id: @topic.last_post) <> "#post_#{@topic.last_post.id}") - =<> link(@topic.title, to: Routes.forum_topic_path(@conn, :show, @topic.forum, @topic)) + = link("replied to", to: ~p"/forums/#{@topic.forum}/topics/#{@topic}?#{[post_id: @topic.last_post]}" <> "#post_#{@topic.last_post.id}") + =<> link(@topic.title, to: ~p"/forums/#{@topic.forum}/topics/#{@topic}") ' in - => link(@topic.forum.name, to: Routes.forum_path(@conn, :show, @topic.forum)) + => link(@topic.forum.name, to: ~p"/forums/#{@topic.forum}") diff --git a/lib/philomena_web/templates/activity/index.html.slime b/lib/philomena_web/templates/activity/index.html.slime index 0abadd9b5..c330d94e2 100644 --- a/lib/philomena_web/templates/activity/index.html.slime +++ b/lib/philomena_web/templates/activity/index.html.slime @@ -32,7 +32,7 @@ ' No artist tags have any associated streams. = if can?(@conn, :create, Philomena.Channels.Channel) do ' As an administrator of this site, you may - => link "click here", to: Routes.channel_path(@conn, :new) + => link "click here", to: ~p"/channels/new" ' to add some, or a href="/channels" ' click here @@ -63,4 +63,4 @@ ' Browse Watched Images .media-list = for image <- @watched do - = render PhilomenaWeb.ImageView, "_image_box.html", image: image, link: Routes.image_path(@conn, :show, image, q: "my:watched"), size: :thumb_small, conn: @conn + = render PhilomenaWeb.ImageView, "_image_box.html", image: image, link: ~p"/images/#{image}?#{[q: "my:watched"]}", size: :thumb_small, conn: @conn diff --git a/lib/philomena_web/templates/admin/advert/_form.html.slime b/lib/philomena_web/templates/admin/advert/_form.html.slime index df77528ee..9fcd46c74 100644 --- a/lib/philomena_web/templates/admin/advert/_form.html.slime +++ b/lib/philomena_web/templates/admin/advert/_form.html.slime @@ -14,7 +14,7 @@ = error_tag f, :image_height - else .field - = link "Change image", to: Routes.admin_advert_image_path(@conn, :edit, @changeset.data), class: "button" + = link "Change image", to: ~p"/admin/adverts/#{@changeset.data}/image/edit", class: "button" .field => label f, :link, "Link which the advert should take users to:" diff --git a/lib/philomena_web/templates/admin/advert/edit.html.slime b/lib/philomena_web/templates/admin/advert/edit.html.slime index fba601a05..a3764720f 100644 --- a/lib/philomena_web/templates/admin/advert/edit.html.slime +++ b/lib/philomena_web/templates/admin/advert/edit.html.slime @@ -1,2 +1,2 @@ h1 Editing advert -= render PhilomenaWeb.Admin.AdvertView, "_form.html", changeset: @changeset, action: Routes.admin_advert_path(@conn, :update, @advert), conn: @conn += render PhilomenaWeb.Admin.AdvertView, "_form.html", changeset: @changeset, action: ~p"/admin/adverts/#{@advert}", conn: @conn diff --git a/lib/philomena_web/templates/admin/advert/image/edit.html.slime b/lib/philomena_web/templates/admin/advert/image/edit.html.slime index 6055e6a35..6374e9c37 100644 --- a/lib/philomena_web/templates/admin/advert/image/edit.html.slime +++ b/lib/philomena_web/templates/admin/advert/image/edit.html.slime @@ -1,6 +1,6 @@ h2 Edit Advert -= form_for @changeset, Routes.admin_advert_image_path(@conn, :update, @advert), [multipart: true], fn f -> += form_for @changeset, ~p"/admin/adverts/#{@advert}/image", [multipart: true], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/admin/advert/index.html.slime b/lib/philomena_web/templates/admin/advert/index.html.slime index 1c7e34d39..609d468e8 100644 --- a/lib/philomena_web/templates/admin/advert/index.html.slime +++ b/lib/philomena_web/templates/admin/advert/index.html.slime @@ -1,9 +1,9 @@ -- route = fn p -> Routes.admin_advert_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/adverts?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @adverts, route: route .block .block__header - a href=Routes.admin_advert_path(@conn, :new) + a href=~p"/admin/adverts/new" i.fa.fa-plus> ' New advert @@ -65,9 +65,9 @@ = advert.clicks td - => link "Edit", to: Routes.admin_advert_path(@conn, :edit, advert) + => link "Edit", to: ~p"/admin/adverts/#{advert}/edit" ' • - = link "Destroy", to: Routes.admin_advert_path(@conn, :delete, advert), data: [confirm: "Are you really, really sure?", method: "delete"] + = link "Destroy", to: ~p"/admin/adverts/#{advert}", data: [confirm: "Are you really, really sure?", method: "delete"] .block__header.block__header--light = pagination diff --git a/lib/philomena_web/templates/admin/advert/new.html.slime b/lib/philomena_web/templates/admin/advert/new.html.slime index ab27e617d..76472e475 100644 --- a/lib/philomena_web/templates/admin/advert/new.html.slime +++ b/lib/philomena_web/templates/admin/advert/new.html.slime @@ -1,2 +1,2 @@ h1 New advert -= render PhilomenaWeb.Admin.AdvertView, "_form.html", changeset: @changeset, action: Routes.admin_advert_path(@conn, :create), conn: @conn += render PhilomenaWeb.Admin.AdvertView, "_form.html", changeset: @changeset, action: ~p"/admin/adverts", conn: @conn diff --git a/lib/philomena_web/templates/admin/approval/_approvals.html.slime b/lib/philomena_web/templates/admin/approval/_approvals.html.slime index 530b4d3b5..a61564ddf 100644 --- a/lib/philomena_web/templates/admin/approval/_approvals.html.slime +++ b/lib/philomena_web/templates/admin/approval/_approvals.html.slime @@ -12,23 +12,23 @@ .block__content.alternating-color .approval-grid .approval-items--main - span = link ">>#{image.id}", to: Routes.image_path(@conn, :show, image) + span = link ">>#{image.id}", to: ~p"/images/#{image}" .approval-items__details class=class_for_image(image) span = image_thumb(@conn, image) span = warning_text(image) span = if image.user do - = link image.user.name, to: Routes.profile_path(@conn, :show, image.user) + = link image.user.name, to: ~p"/profiles/#{image.user}" - else em> - = truncated_ip_link(@conn, image.ip) + = truncated_ip_link(image.ip) = link_to_fingerprint(@conn, image.fingerprint) span = pretty_time(image.created_at) .approval-items--footer = if can?(@conn, :approve, image) do - = button_to "Approve", Routes.image_approve_path(@conn, :create, image), method: "post", class: "button button--success" + = button_to "Approve", ~p"/images/#{image}/approve", method: "post", class: "button button--success" = if can?(@conn, :hide, image) do - = form_for :image, Routes.image_delete_path(@conn, :create, image), [method: "post"], fn f -> + = form_for :image, ~p"/images/#{image}/delete", [method: "post"], fn f -> .field.field--inline = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Rule violation", required: true = submit "Delete", class: "button button--danger button--separate-left" diff --git a/lib/philomena_web/templates/admin/approval/index.html.slime b/lib/philomena_web/templates/admin/approval/index.html.slime index 818f7ae1d..d3f8e0980 100644 --- a/lib/philomena_web/templates/admin/approval/index.html.slime +++ b/lib/philomena_web/templates/admin/approval/index.html.slime @@ -1,4 +1,4 @@ -- route = fn p -> Routes.admin_approval_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/approvals?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @images, route: route h1 Approval Queue diff --git a/lib/philomena_web/templates/admin/artist_link/index.html.slime b/lib/philomena_web/templates/admin/artist_link/index.html.slime index b9d0e8db9..633ff2c18 100644 --- a/lib/philomena_web/templates/admin/artist_link/index.html.slime +++ b/lib/philomena_web/templates/admin/artist_link/index.html.slime @@ -2,20 +2,20 @@ h1 Artist Links p Link creation is done via the Users menu. p Verifying a link will automatically award an artist badge if the link is public, no artist badge exists, and an "artist:" tag is specified. -= form_for :artist_link, Routes.admin_artist_link_path(@conn, :index), [method: "get", class: "hform"], fn f -> += form_for :artist_link, ~p"/admin/artist_links", [method: "get", class: "hform"], fn f -> .field = text_input f, :q, name: :q, value: @conn.params["q"], class: "input hform__text", placeholder: "Search query", autocapitalize: "none" = submit "Search", class: "hform__button button" -- route = fn p -> Routes.admin_artist_link_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/artist_links?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @artist_links, route: route, params: link_scope(@conn), conn: @conn .block .block__header = if @conn.params["all"] do - = link "Show unverified only", to: Routes.admin_artist_link_path(@conn, :index) + = link "Show unverified only", to: ~p"/admin/artist_links" - else - = link "Show all", to: Routes.admin_artist_link_path(@conn, :index, all: "true") + = link "Show all", to: ~p"/admin/artist_links?#{[all: "true"]}" = pagination @@ -56,20 +56,20 @@ p Verifying a link will automatically award an artist badge if the link is publi = render PhilomenaWeb.TagView, "_tag.html", tag: link.tag, conn: @conn td - => link "View", to: Routes.profile_artist_link_path(@conn, :show, link.user, link) + => link "View", to: ~p"/profiles/#{link.user}/artist_links/#{link}" ' • - = link "Edit", to: Routes.profile_artist_link_path(@conn, :edit, link.user, link) + = link "Edit", to: ~p"/profiles/#{link.user}/artist_links/#{link}/edit" td - => link "Verify", to: Routes.admin_artist_link_verification_path(@conn, :create, link), method: :post + => link "Verify", to: ~p"/admin/artist_links/#{link}/verification", method: :post ' • - => link "Reject", to: Routes.admin_artist_link_reject_path(@conn, :create, link), method: :post + => link "Reject", to: ~p"/admin/artist_links/#{link}/reject", method: :post br = if not verified?(link) do = if contacted?(link) do ' Artist contacted - else - = link "Artist contacted", to: Routes.admin_artist_link_contact_path(@conn, :create, link), method: :post + = link "Artist contacted", to: ~p"/admin/artist_links/#{link}/contact", method: :post td = public_text(link) diff --git a/lib/philomena_web/templates/admin/badge/_form.html.slime b/lib/philomena_web/templates/admin/badge/_form.html.slime index d8af18abf..1e5dc79bb 100644 --- a/lib/philomena_web/templates/admin/badge/_form.html.slime +++ b/lib/philomena_web/templates/admin/badge/_form.html.slime @@ -30,6 +30,6 @@ = error_tag f, :image_mime_type - else .field - = link "Change image", to: Routes.admin_badge_image_path(@conn, :edit, @changeset.data), class: "button" + = link "Change image", to: ~p"/admin/badges/#{@changeset.data}/image/edit", class: "button" = submit "Save Badge", class: "button", data: [disable_with: raw("Saving…")] diff --git a/lib/philomena_web/templates/admin/badge/edit.html.slime b/lib/philomena_web/templates/admin/badge/edit.html.slime index 01df2fba4..da713050a 100644 --- a/lib/philomena_web/templates/admin/badge/edit.html.slime +++ b/lib/philomena_web/templates/admin/badge/edit.html.slime @@ -1,5 +1,5 @@ h2 Edit Badge -= render "_form.html", Map.put(assigns, :action, Routes.admin_badge_path(@conn, :update, @badge)) += render "_form.html", Map.put(assigns, :action, ~p"/admin/badges/#{@badge}") -= link "Back", to: Routes.admin_badge_path(@conn, :index) += link "Back", to: ~p"/admin/badges" diff --git a/lib/philomena_web/templates/admin/badge/image/edit.html.slime b/lib/philomena_web/templates/admin/badge/image/edit.html.slime index ca0d7b4ad..90042dbb4 100644 --- a/lib/philomena_web/templates/admin/badge/image/edit.html.slime +++ b/lib/philomena_web/templates/admin/badge/image/edit.html.slime @@ -1,6 +1,6 @@ h2 Edit Badge -= form_for @changeset, Routes.admin_badge_image_path(@conn, :update, @badge), [multipart: true], fn f -> += form_for @changeset, ~p"/admin/badges/#{@badge}/image", [multipart: true], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/admin/badge/index.html.slime b/lib/philomena_web/templates/admin/badge/index.html.slime index b46f6b71f..f39498588 100644 --- a/lib/philomena_web/templates/admin/badge/index.html.slime +++ b/lib/philomena_web/templates/admin/badge/index.html.slime @@ -1,11 +1,11 @@ h2 Badges -- route = fn p -> Routes.admin_badge_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/badges?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @badges, route: route, conn: @conn .block .block__header - a href=Routes.admin_badge_path(@conn, :new) + a href=~p"/admin/badges/new" i.fa.fa-plus> ' New Badge @@ -28,9 +28,9 @@ h2 Badges = badge_image(badge, width: 32, height: 32) td - => link "Users", to: Routes.admin_badge_user_path(@conn, :index, badge) + => link "Users", to: ~p"/admin/badges/#{badge}/users" ' • - = link "Edit", to: Routes.admin_badge_path(@conn, :edit, badge) + = link "Edit", to: ~p"/admin/badges/#{badge}/edit" .block__header.block__header--light = pagination diff --git a/lib/philomena_web/templates/admin/badge/new.html.slime b/lib/philomena_web/templates/admin/badge/new.html.slime index 729622479..57fc82732 100644 --- a/lib/philomena_web/templates/admin/badge/new.html.slime +++ b/lib/philomena_web/templates/admin/badge/new.html.slime @@ -1,5 +1,5 @@ h2 New Badge -= render "_form.html", Map.put(assigns, :action, Routes.admin_badge_path(@conn, :create)) += render "_form.html", Map.put(assigns, :action, ~p"/admin/badges") -= link "Back", to: Routes.admin_badge_path(@conn, :index) += link "Back", to: ~p"/admin/badges" diff --git a/lib/philomena_web/templates/admin/badge/user/index.html.slime b/lib/philomena_web/templates/admin/badge/user/index.html.slime index 8670165e9..2e832c89f 100644 --- a/lib/philomena_web/templates/admin/badge/user/index.html.slime +++ b/lib/philomena_web/templates/admin/badge/user/index.html.slime @@ -3,7 +3,7 @@ h1 => @badge.title ' badge -- route = fn p -> Routes.admin_badge_user_path(@conn, :index, @badge, p) end +- route = fn p -> ~p"/admin/badges/#{@badge}/users?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @users, route: route, conn: @conn .block @@ -19,7 +19,7 @@ h1 = for user <- @users do tr td - = link user.name, to: Routes.profile_path(@conn, :show, user) + = link user.name, to: ~p"/profiles/#{user}" .block__header.block__header--light = pagination diff --git a/lib/philomena_web/templates/admin/dnp_entry/index.html.slime b/lib/philomena_web/templates/admin/dnp_entry/index.html.slime index ee39d9e80..03635f82d 100644 --- a/lib/philomena_web/templates/admin/dnp_entry/index.html.slime +++ b/lib/philomena_web/templates/admin/dnp_entry/index.html.slime @@ -1,11 +1,11 @@ h2 Do-Not-Post Requests -= form_for :dnp_entry, Routes.admin_dnp_entry_path(@conn, :index), [method: "get", class: "hform"], fn f -> += form_for :dnp_entry, ~p"/admin/dnp_entries", [method: "get", class: "hform"], fn f -> .field = text_input f, :q, name: :q, value: @conn.params["q"], class: "input hform__text", placeholder: "Search query", autocapitalize: "none" = submit "Search", class: "hform__button button" -- route = fn p -> Routes.admin_dnp_entry_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/dnp_entries?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @dnp_entries, route: route, params: [states: state_param(@conn.params["states"])] .block @@ -13,10 +13,10 @@ h2 Do-Not-Post Requests = pagination span Display Only: - => link "All Open", to: Routes.admin_dnp_entry_path(@conn, :index, states: ~W(requested claimed rescinded acknowledged)) - => link "Listed", to: Routes.admin_dnp_entry_path(@conn, :index, states: ~W(listed)) - => link "Rescinded", to: Routes.admin_dnp_entry_path(@conn, :index, states: ~W(rescinded acknowledged)) - => link "Closed", to: Routes.admin_dnp_entry_path(@conn, :index, states: ~W(closed)) + => link "All Open", to: ~p"/admin/dnp_entries?#{[states: ~W(requested claimed rescinded acknowledged)]}" + => link "Listed", to: ~p"/admin/dnp_entries?#{[states: ~W(listed)]}" + => link "Rescinded", to: ~p"/admin/dnp_entries?#{[states: ~W(rescinded acknowledged)]}" + => link "Closed", to: ~p"/admin/dnp_entries?#{[states: ~W(closed)]}" .block__content table.table @@ -38,7 +38,7 @@ h2 Do-Not-Post Requests = render PhilomenaWeb.TagView, "_tag.html", tag: request.tag, conn: @conn td - = link request.requesting_user.name, to: Routes.profile_path(@conn, :show, request.requesting_user) + = link request.requesting_user.name, to: ~p"/profiles/#{request.requesting_user}" td = request.dnp_type @@ -51,7 +51,7 @@ h2 Do-Not-Post Requests = if request.modifying_user do ' by - = link request.modifying_user.name, to: Routes.profile_path(@conn, :show, request.modifying_user) + = link request.modifying_user.name, to: ~p"/profiles/#{request.modifying_user}" td = pretty_time(request.created_at) @@ -60,31 +60,31 @@ h2 Do-Not-Post Requests = pretty_time(request.updated_at) td - => link "Show", to: Routes.dnp_entry_path(@conn, :show, request) + => link "Show", to: ~p"/dnp/#{request}" ' • - => link "Send PM", to: Routes.conversation_path(@conn, :new, recipient: request.requesting_user.name) + => link "Send PM", to: ~p"/conversations/new?#{[recipient: request.requesting_user.name]}" = case request.aasm_state do - s when s in ["requested", "claimed"] -> ' • - => link "Claim", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "claimed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Claim", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "claimed"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - => link "Approve", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "listed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Approve", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "listed"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - => link "Close", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "closed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Close", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "closed"]}", data: [method: "post", confirm: "Are you really, really sure?"] - "listed" -> ' • - => link "Rescind", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "rescinded"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Rescind", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "rescinded"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - = link "Close", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "closed"), data: [method: "post", confirm: "Are you really, really sure?"] + = link "Close", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "closed"]}", data: [method: "post", confirm: "Are you really, really sure?"] - s when s in ["rescinded", "acknowledged"] -> ' • - => link "Claim", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "acknowledged"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Claim", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "acknowledged"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - = link "Close", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "closed"), data: [method: "post", confirm: "Are you really, really sure?"] + = link "Close", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "closed"]}", data: [method: "post", confirm: "Are you really, really sure?"] - _state -> ' • - => link "Claim", to: Routes.admin_dnp_entry_transition_path(@conn, :create, request, state: "claimed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Claim", to: ~p"/admin/dnp_entries/#{request}/transition?#{[state: "claimed"]}", data: [method: "post", confirm: "Are you really, really sure?"] diff --git a/lib/philomena_web/templates/admin/donation/_table.html.slime b/lib/philomena_web/templates/admin/donation/_table.html.slime index 7bcd7ac6a..023369b45 100644 --- a/lib/philomena_web/templates/admin/donation/_table.html.slime +++ b/lib/philomena_web/templates/admin/donation/_table.html.slime @@ -12,7 +12,7 @@ table.table tr td = if donation.user do - = link donation.user.name, to: Routes.profile_path(@conn, :show, donation.user) + = link donation.user.name, to: ~p"/profiles/#{donation.user}" td = donation.email diff --git a/lib/philomena_web/templates/admin/donation/index.html.slime b/lib/philomena_web/templates/admin/donation/index.html.slime index f27ed0bbd..97ed6caca 100644 --- a/lib/philomena_web/templates/admin/donation/index.html.slime +++ b/lib/philomena_web/templates/admin/donation/index.html.slime @@ -1,4 +1,4 @@ -- route = fn p -> Routes.admin_donation_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/donations?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @donations, route: route, conn: @conn h1 Donations diff --git a/lib/philomena_web/templates/admin/donation/user/index.html.slime b/lib/philomena_web/templates/admin/donation/user/index.html.slime index dc45ab3e0..daf4f6c12 100644 --- a/lib/philomena_web/templates/admin/donation/user/index.html.slime +++ b/lib/philomena_web/templates/admin/donation/user/index.html.slime @@ -6,7 +6,7 @@ = render PhilomenaWeb.Admin.DonationView, "_table.html", donations: @donations, conn: @conn h1 Add Donation - = form_for @changeset, Routes.admin_donation_path(@conn, :create), fn f -> + = form_for @changeset, ~p"/admin/donations", fn f -> .field => label f, :user_id, "User ID:" = number_input f, :user_id, class: "input input--short", value: @user.id diff --git a/lib/philomena_web/templates/admin/fingerprint_ban/edit.html.slime b/lib/philomena_web/templates/admin/fingerprint_ban/edit.html.slime index 2e78f09b9..eb424776b 100644 --- a/lib/philomena_web/templates/admin/fingerprint_ban/edit.html.slime +++ b/lib/philomena_web/templates/admin/fingerprint_ban/edit.html.slime @@ -1,6 +1,6 @@ h1 Editing ban -= render PhilomenaWeb.Admin.FingerprintBanView, "_form.html", changeset: @changeset, action: Routes.admin_fingerprint_ban_path(@conn, :update, @fingerprint), conn: @conn += render PhilomenaWeb.Admin.FingerprintBanView, "_form.html", changeset: @changeset, action: ~p"/admin/fingerprint_bans/#{@fingerprint}", conn: @conn br -= link "Back", to: Routes.admin_fingerprint_ban_path(@conn, :index) += link "Back", to: ~p"/admin/fingerprint_bans" diff --git a/lib/philomena_web/templates/admin/fingerprint_ban/index.html.slime b/lib/philomena_web/templates/admin/fingerprint_ban/index.html.slime index b173c39a4..3888c7bf2 100644 --- a/lib/philomena_web/templates/admin/fingerprint_ban/index.html.slime +++ b/lib/philomena_web/templates/admin/fingerprint_ban/index.html.slime @@ -1,16 +1,16 @@ h1 Fingerprint Bans -- route = fn p -> Routes.admin_fingerprint_ban_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/fingerprint_bans?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @fingerprint_bans, route: route, params: page_params(@conn.params) -= form_for :fingerprint_ban, Routes.admin_fingerprint_ban_path(@conn, :index), [method: "get", class: "hform"], fn f -> += form_for :fingerprint_ban, ~p"/admin/fingerprint_bans", [method: "get", class: "hform"], fn f -> .field = text_input f, :q, name: "q", class: "hform__text input", placeholder: "Search" = submit "Search", class: "button hform__button" .block .block__header - a href=Routes.admin_fingerprint_ban_path(@conn, :new) + a href=~p"/admin/fingerprint_bans/new" i.fa.fa-plus> ' New fingerprint ban @@ -31,11 +31,11 @@ h1 Fingerprint Bans = for ban <- @fingerprint_bans do tr td - = link ban.fingerprint, to: Routes.fingerprint_profile_path(@conn, :show, ban.fingerprint) + = link ban.fingerprint, to: ~p"/fingerprint_profiles/#{ban.fingerprint}" td => pretty_time ban.created_at - = user_abbrv @conn, ban.banning_user + = user_abbrv(ban.banning_user) td class=ban_row_class(ban) = pretty_time ban.valid_until @@ -53,10 +53,10 @@ h1 Fingerprint Bans = ban.generated_ban_id td - => link "Edit", to: Routes.admin_fingerprint_ban_path(@conn, :edit, ban) + => link "Edit", to: ~p"/admin/fingerprint_bans/#{ban}/edit" = if @current_user.role == "admin" do ' • - => link "Destroy", to: Routes.admin_fingerprint_ban_path(@conn, :delete, ban), data: [confirm: "Are you really, really sure?", method: "delete"] + => link "Destroy", to: ~p"/admin/fingerprint_bans/#{ban}", data: [confirm: "Are you really, really sure?", method: "delete"] .block__header.block__header--light = pagination diff --git a/lib/philomena_web/templates/admin/fingerprint_ban/new.html.slime b/lib/philomena_web/templates/admin/fingerprint_ban/new.html.slime index 75db4f622..02d20b60e 100644 --- a/lib/philomena_web/templates/admin/fingerprint_ban/new.html.slime +++ b/lib/philomena_web/templates/admin/fingerprint_ban/new.html.slime @@ -1,5 +1,5 @@ h1 New Fingerprint Ban -= render PhilomenaWeb.Admin.FingerprintBanView, "_form.html", changeset: @changeset, action: Routes.admin_fingerprint_ban_path(@conn, :create), conn: @conn += render PhilomenaWeb.Admin.FingerprintBanView, "_form.html", changeset: @changeset, action: ~p"/admin/fingerprint_bans", conn: @conn br -= link "Back", to: Routes.admin_fingerprint_ban_path(@conn, :index) += link "Back", to: ~p"/admin/fingerprint_bans" diff --git a/lib/philomena_web/templates/admin/forum/edit.html.slime b/lib/philomena_web/templates/admin/forum/edit.html.slime index 4f513a095..8d0e959b3 100644 --- a/lib/philomena_web/templates/admin/forum/edit.html.slime +++ b/lib/philomena_web/templates/admin/forum/edit.html.slime @@ -1,5 +1,5 @@ h2 Edit Forum -= render "_form.html", Map.put(assigns, :action, Routes.admin_forum_path(@conn, :update, @forum)) += render "_form.html", Map.put(assigns, :action, ~p"/admin/forums/#{@forum}") -= link "Back", to: Routes.admin_forum_path(@conn, :index) += link "Back", to: ~p"/admin/forums" diff --git a/lib/philomena_web/templates/admin/forum/index.html.slime b/lib/philomena_web/templates/admin/forum/index.html.slime index fec3c55c6..c1423ecec 100644 --- a/lib/philomena_web/templates/admin/forum/index.html.slime +++ b/lib/philomena_web/templates/admin/forum/index.html.slime @@ -9,9 +9,9 @@ table.table = for forum <- @forums do tr td - = link forum.name, to: Routes.forum_path(@conn, :show, forum) + = link forum.name, to: ~p"/forums/#{forum}" td class="text-right" - = link "Edit", to: Routes.admin_forum_path(@conn, :edit, forum) + = link "Edit", to: ~p"/admin/forums/#{forum}/edit" -= link "New Forum", to: Routes.admin_forum_path(@conn, :new) += link "New Forum", to: ~p"/admin/forums/new" diff --git a/lib/philomena_web/templates/admin/forum/new.html.slime b/lib/philomena_web/templates/admin/forum/new.html.slime index 2d6fd0cd6..602d7d143 100644 --- a/lib/philomena_web/templates/admin/forum/new.html.slime +++ b/lib/philomena_web/templates/admin/forum/new.html.slime @@ -1,5 +1,5 @@ h2 New Forum -= render "_form.html", Map.put(assigns, :action, Routes.admin_forum_path(@conn, :create)) += render "_form.html", Map.put(assigns, :action, ~p"/admin/forums") -= link "Back", to: Routes.admin_forum_path(@conn, :index) += link "Back", to: ~p"/admin/forums" diff --git a/lib/philomena_web/templates/admin/mod_note/_table.html.slime b/lib/philomena_web/templates/admin/mod_note/_table.html.slime index a47663a75..452078009 100644 --- a/lib/philomena_web/templates/admin/mod_note/_table.html.slime +++ b/lib/philomena_web/templates/admin/mod_note/_table.html.slime @@ -10,7 +10,7 @@ table.table = for {body, note} <- @mod_notes do tr td - = link_to_noted_thing(@conn, note.notable) + = link_to_noted_thing(note.notable) td = body @@ -19,9 +19,9 @@ table.table = pretty_time note.created_at td - = link note.moderator.name, to: Routes.profile_path(@conn, :show, note.moderator) + = link note.moderator.name, to: ~p"/profiles/#{note.moderator}" td - => link "Edit", to: Routes.admin_mod_note_path(@conn, :edit, note) + => link "Edit", to: ~p"/admin/mod_notes/#{note}/edit" ' • - => link "Delete", to: Routes.admin_mod_note_path(@conn, :delete, note), data: [confirm: "Are you really, really sure?", method: "delete"] + => link "Delete", to: ~p"/admin/mod_notes/#{note}", data: [confirm: "Are you really, really sure?", method: "delete"] diff --git a/lib/philomena_web/templates/admin/mod_note/edit.html.slime b/lib/philomena_web/templates/admin/mod_note/edit.html.slime index e9df55566..aed68f42c 100644 --- a/lib/philomena_web/templates/admin/mod_note/edit.html.slime +++ b/lib/philomena_web/templates/admin/mod_note/edit.html.slime @@ -3,4 +3,4 @@ h2 => @mod_note.notable_type => @mod_note.notable_id -= render PhilomenaWeb.Admin.ModNoteView, "_form.html", changeset: @changeset, action: Routes.admin_mod_note_path(@conn, :update, @mod_note), conn: @conn += render PhilomenaWeb.Admin.ModNoteView, "_form.html", changeset: @changeset, action: ~p"/admin/mod_notes/#{@mod_note}", conn: @conn diff --git a/lib/philomena_web/templates/admin/mod_note/index.html.slime b/lib/philomena_web/templates/admin/mod_note/index.html.slime index fed174208..4c005f9e2 100644 --- a/lib/philomena_web/templates/admin/mod_note/index.html.slime +++ b/lib/philomena_web/templates/admin/mod_note/index.html.slime @@ -1,4 +1,4 @@ -- route = fn p -> Routes.admin_mod_note_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/mod_notes?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @mod_notes, route: route, conn: @conn h2 Mod Notes diff --git a/lib/philomena_web/templates/admin/mod_note/new.html.slime b/lib/philomena_web/templates/admin/mod_note/new.html.slime index 1cb13aeac..09d7291e5 100644 --- a/lib/philomena_web/templates/admin/mod_note/new.html.slime +++ b/lib/philomena_web/templates/admin/mod_note/new.html.slime @@ -3,4 +3,4 @@ h2 => @conn.params["notable_type"] => @conn.params["notable_id"] -= render PhilomenaWeb.Admin.ModNoteView, "_form.html", changeset: @changeset, action: Routes.admin_mod_note_path(@conn, :create), conn: @conn += render PhilomenaWeb.Admin.ModNoteView, "_form.html", changeset: @changeset, action: ~p"/admin/mod_notes", conn: @conn diff --git a/lib/philomena_web/templates/admin/report/_reports.html.slime b/lib/philomena_web/templates/admin/report/_reports.html.slime index 773a20723..c80176e3c 100644 --- a/lib/philomena_web/templates/admin/report/_reports.html.slime +++ b/lib/philomena_web/templates/admin/report/_reports.html.slime @@ -12,16 +12,16 @@ table.table tr td => reported_image @conn, report.reportable - = link_to_reported_thing @conn, report.reportable + = link_to_reported_thing(report.reportable) td span title=report.reason = truncate(report.reason) td = if report.user do - = link report.user.name, to: Routes.profile_path(@conn, :show, report.user) + = link report.user.name, to: ~p"/profiles/#{report.user}" - else em> - = truncated_ip_link(@conn, report.ip) + = truncated_ip_link(report.ip) = link_to_fingerprint(@conn, report.fingerprint) = if not is_nil(report.user) and Enum.any?(report.user.linked_tags) do @@ -32,22 +32,22 @@ table.table td class=report_row_class(report) => pretty_state(report) - = user_abbrv @conn, report.admin + = user_abbrv(report.admin) td - => link "Show", to: Routes.admin_report_path(@conn, :show, report) + => link "Show", to: ~p"/admin/reports/#{report}" = if report.open do = if report.user do ' • - => link "Send PM", to: Routes.conversation_path(@conn, :new, recipient: report.user.name) + => link "Send PM", to: ~p"/conversations/new?#{[recipient: report.user.name]}" = if is_nil(report.admin) and not current?(report.admin, @conn.assigns.current_user) do ' • - => link "Claim", to: Routes.admin_report_claim_path(@conn, :create, report), data: [method: "post"] + => link "Claim", to: ~p"/admin/reports/#{report}/claim", data: [method: "post"] = if current?(report.admin, @conn.assigns.current_user) do ' • - => link "Release", to: Routes.admin_report_claim_path(@conn, :delete, report), data: [method: "delete"] + => link "Release", to: ~p"/admin/reports/#{report}/claim", data: [method: "delete"] ' • - => link "Close", to: Routes.admin_report_close_path(@conn, :create, report), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Close", to: ~p"/admin/reports/#{report}/close", data: [method: "post", confirm: "Are you really, really sure?"] diff --git a/lib/philomena_web/templates/admin/report/index.html.slime b/lib/philomena_web/templates/admin/report/index.html.slime index d65af25c5..bd467e234 100644 --- a/lib/philomena_web/templates/admin/report/index.html.slime +++ b/lib/philomena_web/templates/admin/report/index.html.slime @@ -1,4 +1,4 @@ -- route = fn p -> Routes.admin_report_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/reports?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", route: route, page: @reports, conn: @conn, params: [rq: @conn.params["rq"] || "*"] h1 Reports @@ -30,7 +30,7 @@ h1 Reports .block__header.block__header--light = pagination -= form_for :report, Routes.admin_report_path(@conn, :index), [method: "get", class: "hform"], fn f -> += form_for :report, ~p"/admin/reports", [method: "get", class: "hform"], fn f -> .field = text_input f, :rq, name: :rq, value: @conn.params["rq"], class: "input hform__text", placeholder: "Search reports", autocapitalize: "none" = submit "Search", class: "hform__button button" diff --git a/lib/philomena_web/templates/admin/report/show.html.slime b/lib/philomena_web/templates/admin/report/show.html.slime index 4b819f943..9c227fae7 100644 --- a/lib/philomena_web/templates/admin/report/show.html.slime +++ b/lib/philomena_web/templates/admin/report/show.html.slime @@ -1,6 +1,6 @@ h1 Showing Report p - = link_to_reported_thing @conn, @report.reportable + = link_to_reported_thing(@report.reportable) article.block.communication .block__content.flex.flex--no-wrap @@ -24,18 +24,18 @@ article.block.communication = if assigns[:mod_notes] do h4 Mod Notes = render PhilomenaWeb.Admin.ModNoteView, "_table.html", mod_notes: @mod_notes, conn: @conn - = link "Add New Note", to: Routes.admin_mod_note_path(@conn, :new, notable_id: @report.id, notable_type: "Report") + = link "Add New Note", to: ~p"/admin/mod_notes/new?#{[notable_id: @report.id, notable_type: "Report"]}" p = if @report.user do - => link "Send PM", to: Routes.conversation_path(@conn, :new, recipient: @report.user.name), class: "button" + => link "Send PM", to: ~p"/conversations/new?#{[recipient: @report.user.name]}", class: "button" = if @report.open do - => link "Close", to: Routes.admin_report_close_path(@conn, :create, @report), class: "button", data: [method: "post"] + => link "Close", to: ~p"/admin/reports/#{@report}/close", class: "button", data: [method: "post"] = if current?(@report.admin, @conn.assigns.current_user) do - => link "Release", to: Routes.admin_report_claim_path(@conn, :delete, @report), class: "button", data: [method: "delete"] + => link "Release", to: ~p"/admin/reports/#{@report}/claim", class: "button", data: [method: "delete"] - else - => link "Claim", to: Routes.admin_report_claim_path(@conn, :create, @report), class: "button", data: [method: "post"] + => link "Claim", to: ~p"/admin/reports/#{@report}/claim", class: "button", data: [method: "post"] -= link "Back", to: Routes.admin_report_path(@conn, :index), class: "button button-link" += link "Back", to: ~p"/admin/reports", class: "button button-link" diff --git a/lib/philomena_web/templates/admin/site_notice/edit.html.slime b/lib/philomena_web/templates/admin/site_notice/edit.html.slime index bd745654b..de39c73e4 100644 --- a/lib/philomena_web/templates/admin/site_notice/edit.html.slime +++ b/lib/philomena_web/templates/admin/site_notice/edit.html.slime @@ -1,2 +1,2 @@ h1 Editing site notice -= render PhilomenaWeb.Admin.SiteNoticeView, "_form.html", changeset: @changeset, action: Routes.admin_site_notice_path(@conn, :update, @site_notice), conn: @conn += render PhilomenaWeb.Admin.SiteNoticeView, "_form.html", changeset: @changeset, action: ~p"/admin/site_notices/#{@site_notice}", conn: @conn diff --git a/lib/philomena_web/templates/admin/site_notice/index.html.slime b/lib/philomena_web/templates/admin/site_notice/index.html.slime index 1ff2ee3fd..21cac7671 100644 --- a/lib/philomena_web/templates/admin/site_notice/index.html.slime +++ b/lib/philomena_web/templates/admin/site_notice/index.html.slime @@ -1,11 +1,11 @@ h1 Site Notices -- route = fn p -> Routes.admin_site_notice_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/site_notices?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @admin_site_notices, route: route, conn: @conn .block .block__header - a href=Routes.admin_site_notice_path(@conn, :new) + a href=~p"/admin/site_notices/new" i.fa.fa-plus> ' New site notice @@ -36,9 +36,9 @@ h1 Site Notices = live_text site_notice td - => link "Edit", to: Routes.admin_site_notice_path(@conn, :edit, site_notice) + => link "Edit", to: ~p"/admin/site_notices/#{site_notice}/edit" ' • - => link "Destroy", to: Routes.admin_site_notice_path(@conn, :delete, site_notice), data: [confirm: "Are you really, really sure?", method: "delete"] + => link "Destroy", to: ~p"/admin/site_notices/#{site_notice}", data: [confirm: "Are you really, really sure?", method: "delete"] .block__header.block__header--light = pagination diff --git a/lib/philomena_web/templates/admin/site_notice/new.html.slime b/lib/philomena_web/templates/admin/site_notice/new.html.slime index b8c2f34b3..cf85b7933 100644 --- a/lib/philomena_web/templates/admin/site_notice/new.html.slime +++ b/lib/philomena_web/templates/admin/site_notice/new.html.slime @@ -1,2 +1,2 @@ h1 New site notice -= render PhilomenaWeb.Admin.SiteNoticeView, "_form.html", changeset: @changeset, action: Routes.admin_site_notice_path(@conn, :create), conn: @conn += render PhilomenaWeb.Admin.SiteNoticeView, "_form.html", changeset: @changeset, action: ~p"/admin/site_notices", conn: @conn diff --git a/lib/philomena_web/templates/admin/subnet_ban/edit.html.slime b/lib/philomena_web/templates/admin/subnet_ban/edit.html.slime index 98f356343..dc4519832 100644 --- a/lib/philomena_web/templates/admin/subnet_ban/edit.html.slime +++ b/lib/philomena_web/templates/admin/subnet_ban/edit.html.slime @@ -1,6 +1,6 @@ h1 Editing ban -= render PhilomenaWeb.Admin.SubnetBanView, "_form.html", changeset: @changeset, action: Routes.admin_subnet_ban_path(@conn, :update, @subnet), conn: @conn += render PhilomenaWeb.Admin.SubnetBanView, "_form.html", changeset: @changeset, action: ~p"/admin/subnet_bans/#{@subnet}", conn: @conn br -= link "Back", to: Routes.admin_subnet_ban_path(@conn, :index) += link "Back", to: ~p"/admin/subnet_bans" diff --git a/lib/philomena_web/templates/admin/subnet_ban/index.html.slime b/lib/philomena_web/templates/admin/subnet_ban/index.html.slime index f6b4dc5da..162d1342a 100644 --- a/lib/philomena_web/templates/admin/subnet_ban/index.html.slime +++ b/lib/philomena_web/templates/admin/subnet_ban/index.html.slime @@ -1,16 +1,16 @@ h1 Subnet Bans -- route = fn p -> Routes.admin_subnet_ban_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/subnet_bans?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @subnet_bans, route: route, params: page_params(@conn.params) -= form_for :subnet_ban, Routes.admin_subnet_ban_path(@conn, :index), [method: "get", class: "hform"], fn f -> += form_for :subnet_ban, ~p"/admin/subnet_bans", [method: "get", class: "hform"], fn f -> .field = text_input f, :q, name: "q", class: "hform__text input", placeholder: "Search" = submit "Search", class: "button hform__button" .block .block__header - a href=Routes.admin_subnet_ban_path(@conn, :new) + a href=~p"/admin/subnet_bans/new" i.fa.fa-plus> ' New subnet ban @@ -31,11 +31,11 @@ h1 Subnet Bans = for ban <- @subnet_bans do tr td - = link ban.specification, to: Routes.ip_profile_path(@conn, :show, to_string(ban.specification)) + = link ban.specification, to: ~p"/ip_profiles/#{to_string(ban.specification)}" td => pretty_time ban.created_at - = user_abbrv @conn, ban.banning_user + = user_abbrv(ban.banning_user) td class=ban_row_class(ban) = pretty_time ban.valid_until @@ -53,10 +53,10 @@ h1 Subnet Bans = ban.generated_ban_id td - => link "Edit", to: Routes.admin_subnet_ban_path(@conn, :edit, ban) + => link "Edit", to: ~p"/admin/subnet_bans/#{ban}/edit" = if @current_user.role == "admin" do ' • - => link "Destroy", to: Routes.admin_subnet_ban_path(@conn, :delete, ban), data: [confirm: "Are you really, really sure?", method: "delete"] + => link "Destroy", to: ~p"/admin/subnet_bans/#{ban}", data: [confirm: "Are you really, really sure?", method: "delete"] .block__header.block__header--light = pagination diff --git a/lib/philomena_web/templates/admin/subnet_ban/new.html.slime b/lib/philomena_web/templates/admin/subnet_ban/new.html.slime index 50d01a785..7732231b2 100644 --- a/lib/philomena_web/templates/admin/subnet_ban/new.html.slime +++ b/lib/philomena_web/templates/admin/subnet_ban/new.html.slime @@ -1,5 +1,5 @@ h1 New Subnet Ban -= render PhilomenaWeb.Admin.SubnetBanView, "_form.html", changeset: @changeset, action: Routes.admin_subnet_ban_path(@conn, :create), conn: @conn += render PhilomenaWeb.Admin.SubnetBanView, "_form.html", changeset: @changeset, action: ~p"/admin/subnet_bans", conn: @conn br -= link "Back", to: Routes.admin_subnet_ban_path(@conn, :index) += link "Back", to: ~p"/admin/subnet_bans" diff --git a/lib/philomena_web/templates/admin/user/_form.html.slime b/lib/philomena_web/templates/admin/user/_form.html.slime index a89a8347c..7d529f24e 100644 --- a/lib/philomena_web/templates/admin/user/_form.html.slime +++ b/lib/philomena_web/templates/admin/user/_form.html.slime @@ -27,7 +27,7 @@ .field label Avatar .with-error - = link to: Routes.admin_user_avatar_path(@conn, :delete, @user), class: "button button--danger", data: [method: "delete", confirm: "Are you really, really sure?"] do + = link to: ~p"/admin/users/#{@user}/avatar", class: "button button--danger", data: [method: "delete", confirm: "Are you really, really sure?"] do i.fa.fa-trash | Remove diff --git a/lib/philomena_web/templates/admin/user/edit.html.slime b/lib/philomena_web/templates/admin/user/edit.html.slime index bb1e6d029..e7a649de5 100644 --- a/lib/philomena_web/templates/admin/user/edit.html.slime +++ b/lib/philomena_web/templates/admin/user/edit.html.slime @@ -1,3 +1,3 @@ h1 Editing user -= render PhilomenaWeb.Admin.UserView, "_form.html", Map.put(assigns, :action, Routes.admin_user_path(@conn, :update, @user)) += render PhilomenaWeb.Admin.UserView, "_form.html", Map.put(assigns, :action, ~p"/admin/users/#{@user}") diff --git a/lib/philomena_web/templates/admin/user/force_filter/new.html.slime b/lib/philomena_web/templates/admin/user/force_filter/new.html.slime index 87533d781..d7bd12e3b 100644 --- a/lib/philomena_web/templates/admin/user/force_filter/new.html.slime +++ b/lib/philomena_web/templates/admin/user/force_filter/new.html.slime @@ -2,7 +2,7 @@ h1 ' Force-assigning a filter for user = @user.name -= form_for @changeset, Routes.admin_user_force_filter_path(@conn, :create, @user), [method: "post"], fn f -> += form_for @changeset, ~p"/admin/users/#{@user}/force_filter", [method: "post"], fn f -> .field => text_input f, :forced_filter_id, placeholder: "Filter ID", class: "input", required: true .field diff --git a/lib/philomena_web/templates/admin/user/index.html.slime b/lib/philomena_web/templates/admin/user/index.html.slime index 6378abd77..29ceb803b 100644 --- a/lib/philomena_web/templates/admin/user/index.html.slime +++ b/lib/philomena_web/templates/admin/user/index.html.slime @@ -1,15 +1,15 @@ h1 Users -= form_for :user, Routes.admin_user_path(@conn, :index), [method: "get", class: "hform"], fn f -> += form_for :user, ~p"/admin/users", [method: "get", class: "hform"], fn f -> .field => text_input f, :q, name: "q", class: "hform__text input", placeholder: "Search query" = submit "Search", class: "button hform__button" -=> link "Site staff", to: Routes.admin_user_path(@conn, :index, staff: 1) +=> link "Site staff", to: ~p"/admin/users?#{[staff: 1]}" ' • -=> link "2FA users", to: Routes.admin_user_path(@conn, :index, twofactor: 1) +=> link "2FA users", to: ~p"/admin/users?#{[twofactor: 1]}" -- route = fn p -> Routes.admin_user_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/users?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @users, route: route, conn: @conn, params: page_params(@conn.params) .block @@ -30,7 +30,7 @@ h1 Users = for user <- @users do tr td - = link user.name, to: Routes.profile_path(@conn, :show, user) + = link user.name, to: ~p"/profiles/#{user}" = cond do - user.otp_required_for_login -> @@ -71,7 +71,7 @@ h1 Users td = if can?(@conn, :edit, user) do - => link to: Routes.admin_user_path(@conn, :edit, user) do + => link to: ~p"/admin/users/#{user}/edit" do i.fa.icon--padded.small.fa-user-edit ' Edit @@ -82,11 +82,11 @@ h1 Users /' • = if can?(@conn, :index, Philomena.Bans.User) do - => link to: Routes.admin_user_ban_path(@conn, :new, username: user.name) do + => link to: ~p"/admin/user_bans/new?#{[username: user.name]}" do i.fa.icon--padded.small.fa-ban ' Ban = if can?(@conn, :edit, Philomena.ArtistLinks.ArtistLink) do - => link to: Routes.profile_artist_link_path(@conn, :new, user) do + => link to: ~p"/profiles/#{user}/artist_links/new" do i.fa.icon--padded.small.fa-link ' Add link diff --git a/lib/philomena_web/templates/admin/user_ban/edit.html.slime b/lib/philomena_web/templates/admin/user_ban/edit.html.slime index ff4f23bb1..604f0ed60 100644 --- a/lib/philomena_web/templates/admin/user_ban/edit.html.slime +++ b/lib/philomena_web/templates/admin/user_ban/edit.html.slime @@ -1,6 +1,6 @@ h1 Editing ban -= render PhilomenaWeb.Admin.UserBanView, "_form.html", changeset: @changeset, action: Routes.admin_user_ban_path(@conn, :update, @user), conn: @conn += render PhilomenaWeb.Admin.UserBanView, "_form.html", changeset: @changeset, action: ~p"/admin/user_bans/#{@user}", conn: @conn br -= link "Back", to: Routes.admin_user_ban_path(@conn, :index) += link "Back", to: ~p"/admin/user_bans" diff --git a/lib/philomena_web/templates/admin/user_ban/index.html.slime b/lib/philomena_web/templates/admin/user_ban/index.html.slime index c81d4ab21..61087b58e 100644 --- a/lib/philomena_web/templates/admin/user_ban/index.html.slime +++ b/lib/philomena_web/templates/admin/user_ban/index.html.slime @@ -1,16 +1,16 @@ h1 User Bans -- route = fn p -> Routes.admin_user_ban_path(@conn, :index, p) end +- route = fn p -> ~p"/admin/user_bans?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @user_bans, route: route, params: page_params(@conn.params) -= form_for :user_ban, Routes.admin_user_ban_path(@conn, :index), [method: "get", class: "hform"], fn f -> += form_for :user_ban, ~p"/admin/user_bans", [method: "get", class: "hform"], fn f -> .field = text_input f, :q, name: "q", class: "hform__text input", placeholder: "Search" = submit "Search", class: "button hform__button" .block .block__header - a href=Routes.admin_user_ban_path(@conn, :new) + a href=~p"/admin/user_bans/new" i.fa.fa-plus> ' New user ban @@ -31,11 +31,11 @@ h1 User Bans = for ban <- @user_bans do tr td - = link ban.user.name, to: Routes.profile_path(@conn, :show, ban.user) + = link ban.user.name, to: ~p"/profiles/#{ban.user}" td => pretty_time ban.created_at - = user_abbrv @conn, ban.banning_user + = user_abbrv(ban.banning_user) td class=ban_row_class(ban) = pretty_time ban.valid_until @@ -53,10 +53,10 @@ h1 User Bans = ban.generated_ban_id td - => link "Edit", to: Routes.admin_user_ban_path(@conn, :edit, ban) + => link "Edit", to: ~p"/admin/user_bans/#{ban}/edit" = if @current_user.role == "admin" do ' • - => link "Destroy", to: Routes.admin_user_ban_path(@conn, :delete, ban), data: [confirm: "Are you really, really sure?", method: "delete"] + => link "Destroy", to: ~p"/admin/user_bans/#{ban}", data: [confirm: "Are you really, really sure?", method: "delete"] .block__header.block__header--light = pagination diff --git a/lib/philomena_web/templates/admin/user_ban/new.html.slime b/lib/philomena_web/templates/admin/user_ban/new.html.slime index 468595cb0..cdd82ff88 100644 --- a/lib/philomena_web/templates/admin/user_ban/new.html.slime +++ b/lib/philomena_web/templates/admin/user_ban/new.html.slime @@ -1,5 +1,5 @@ h1 New User Ban -= render PhilomenaWeb.Admin.UserBanView, "_form.html", changeset: @changeset, action: Routes.admin_user_ban_path(@conn, :create), conn: @conn += render PhilomenaWeb.Admin.UserBanView, "_form.html", changeset: @changeset, action: ~p"/admin/user_bans", conn: @conn br -= link "Back", to: Routes.admin_user_ban_path(@conn, :index) += link "Back", to: ~p"/admin/user_bans" diff --git a/lib/philomena_web/templates/advert/_box.html.slime b/lib/philomena_web/templates/advert/_box.html.slime index 77a2e77b1..e66d2a27d 100644 --- a/lib/philomena_web/templates/advert/_box.html.slime +++ b/lib/philomena_web/templates/advert/_box.html.slime @@ -6,7 +6,7 @@ => link "Click here", to: "/pages/advertising" ' for information! - a#imagespns__link href=Routes.advert_path(@conn, :show, @advert) rel="nofollow" title=@advert.title + a#imagespns__link href=~p"/adverts/#{@advert}" rel="nofollow" title=@advert.title img src=advert_image_url(@advert) alt=@advert.title p diff --git a/lib/philomena_web/templates/api/rss/watched/index.html.eex b/lib/philomena_web/templates/api/rss/watched/index.html.eex index cf70bb693..dc8334d3c 100644 --- a/lib/philomena_web/templates/api/rss/watched/index.html.eex +++ b/lib/philomena_web/templates/api/rss/watched/index.html.eex @@ -3,7 +3,7 @@ <%= site_name() %> Watchlist Your watched tags feed from <%= site_name() %> - <%= Routes.api_rss_watched_url(@conn, :index) %> + <%= url(~p"/api/v1/rss/watched") %> <%= last_build_date() %> <%= for image <- @images do %> @@ -12,14 +12,14 @@ - + "> <%= mouseovertext %> ]]> <%= DateTime.to_iso8601(image.created_at) %> - <%= Routes.image_url(@conn, :show, image) %> - <%= Routes.image_url(@conn, :show, image) %> + <%= url(~p"/images/#{image}") %> + <%= url(~p"/images/#{image}") %> <% end %> diff --git a/lib/philomena_web/templates/avatar/edit.html.slime b/lib/philomena_web/templates/avatar/edit.html.slime index 81bd63f58..3ee56bafb 100644 --- a/lib/philomena_web/templates/avatar/edit.html.slime +++ b/lib/philomena_web/templates/avatar/edit.html.slime @@ -8,7 +8,7 @@ p Add a new avatar or remove your existing one here. p Avatars must be less than 1000px tall and wide, and smaller than 300 kilobytes in size. PNG, JPEG, and GIF are acceptable. - = form_for @changeset, Routes.avatar_path(@conn, :update), [method: "put", multipart: true], fn f -> + = form_for @changeset, ~p"/avatar", [method: "put", multipart: true], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -37,10 +37,10 @@ => submit "Update my avatar", class: "button" br - = button_to "Remove my avatar", Routes.avatar_path(@conn, :delete), method: "delete", class: "button", data: [confirm: "Are you really, really sure?"] + = button_to "Remove my avatar", ~p"/avatar", method: "delete", class: "button", data: [confirm: "Are you really, really sure?"] br = if blank?(@conn.params["profile"]) do - = link "Back", to: Routes.registration_path(@conn, :edit) + = link "Back", to: ~p"/registrations/edit" - else - = link "Back", to: Routes.profile_path(@conn, :show, @current_user) + = link "Back", to: ~p"/profiles/#{@current_user}" diff --git a/lib/philomena_web/templates/channel/_channel_box.html.slime b/lib/philomena_web/templates/channel/_channel_box.html.slime index 3a3ce14c7..b949406d7 100644 --- a/lib/philomena_web/templates/channel/_channel_box.html.slime +++ b/lib/philomena_web/templates/channel/_channel_box.html.slime @@ -1,7 +1,7 @@ - link_class = "media-box__header media-box__header--channel media-box__header--link" .media-box - a.media-box__header.media-box__header--channel.media-box__header--link href=Routes.channel_path(@conn, :show, @channel) title=@channel.title + a.media-box__header.media-box__header--channel.media-box__header--link href=~p"/channels/#{@channel}" title=@channel.title = @channel.title || @channel.short_name .media-box__header.media-box__header--channel @@ -17,22 +17,22 @@ | NSFW .media-box__content.media-box__content--channel - a href=Routes.channel_path(@conn, :show, @channel) + a href=~p"/channels/#{@channel}" .image-constrained.media-box__content--channel img src=channel_image(@channel) alt="#{@channel.title}" = if @channel.associated_artist_tag do - a href=Routes.tag_path(@conn, :show, @channel.associated_artist_tag) class=link_class + a href=~p"/tags/#{@channel.associated_artist_tag}" class=link_class i.fa.icon--padded.small.fa-tags> = @channel.associated_artist_tag.name - else .media-box__header.media-box__header--channel No artist tag = if can?(@conn, :edit, @channel) do - a href=Routes.channel_path(@conn, :edit, @channel) class=link_class + a href=~p"/channels/#{@channel}/edit" class=link_class i.fas.icon--padded.small.fa-edit> ' Edit - a href=Routes.channel_path(@conn, :delete, @channel) class=link_class data-method="delete" data-confirm="Are you really, really sure?" + a href=~p"/channels/#{@channel}" class=link_class data-method="delete" data-confirm="Are you really, really sure?" i.fas.icon--padded.small.fa-trash> ' Delete diff --git a/lib/philomena_web/templates/channel/edit.html.slime b/lib/philomena_web/templates/channel/edit.html.slime index 47f0e53ba..6eb25d312 100644 --- a/lib/philomena_web/templates/channel/edit.html.slime +++ b/lib/philomena_web/templates/channel/edit.html.slime @@ -1,3 +1,3 @@ h1 Editing Channel -= render PhilomenaWeb.ChannelView, "_form.html", changeset: @changeset, action: Routes.channel_path(@conn, :update, @channel), conn: @conn += render PhilomenaWeb.ChannelView, "_form.html", changeset: @changeset, action: ~p"/channels/#{@channel}", conn: @conn diff --git a/lib/philomena_web/templates/channel/index.html.slime b/lib/philomena_web/templates/channel/index.html.slime index 7fd24c1ca..50b0bbaa7 100644 --- a/lib/philomena_web/templates/channel/index.html.slime +++ b/lib/philomena_web/templates/channel/index.html.slime @@ -1,9 +1,9 @@ h1 Livestreams -- route = fn p -> Routes.channel_path(@conn, :index, p) end +- route = fn p -> ~p"/channels?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @channels, route: route, conn: @conn, params: [cq: @conn.params["cq"]] -= form_for :channels, Routes.channel_path(@conn, :index), [method: "get", class: "hform", enforce_utf8: false], fn f -> += form_for :channels, ~p"/channels", [method: "get", class: "hform", enforce_utf8: false], fn f -> .field = text_input f, :cq, name: :cq, value: @conn.params["cq"], class: "input hform__text", placeholder: "Search channels", autocapitalize: "none" = submit "Search", class: "hform__button button" @@ -13,11 +13,11 @@ h1 Livestreams .page__pagination = pagination = if @conn.cookies["chan_nsfw"] == "true" do - a href=Routes.channel_nsfw_path(@conn, :delete) data-method="delete" + a href=~p"/channels/nsfw" data-method="delete" i.fa.fa-eye-slash> ' Hide NSFW streams - else - a href=Routes.channel_nsfw_path(@conn, :create) data-method="create" + a href=~p"/channels/nsfw" data-method="create" i.fa.fa-eye> ' Show NSFW streams @@ -30,7 +30,7 @@ h1 Livestreams br = if can?(@conn, :create, Philomena.Channels.Channel) do - = link "New Channel", to: Routes.channel_path(@conn, :new) + = link "New Channel", to: ~p"/channels/new" h2 FAQ p diff --git a/lib/philomena_web/templates/channel/new.html.slime b/lib/philomena_web/templates/channel/new.html.slime index 8a3551e90..a7361ef7c 100644 --- a/lib/philomena_web/templates/channel/new.html.slime +++ b/lib/philomena_web/templates/channel/new.html.slime @@ -1,3 +1,3 @@ h1 Adding Channel -= render PhilomenaWeb.ChannelView, "_form.html", changeset: @changeset, action: Routes.channel_path(@conn, :create), conn: @conn += render PhilomenaWeb.ChannelView, "_form.html", changeset: @changeset, action: ~p"/channels", conn: @conn diff --git a/lib/philomena_web/templates/channel/subscription/_subscription.html.slime b/lib/philomena_web/templates/channel/subscription/_subscription.html.slime index 663ecff2f..f5a171f4c 100644 --- a/lib/philomena_web/templates/channel/subscription/_subscription.html.slime +++ b/lib/philomena_web/templates/channel/subscription/_subscription.html.slime @@ -1,8 +1,8 @@ elixir: - watch_path = Routes.channel_subscription_path(@conn, :create, @channel) + watch_path = ~p"/channels/#{@channel}/subscription" watch_class = if @watching, do: "hidden", else: "" - unwatch_path = Routes.channel_subscription_path(@conn, :delete, @channel) + unwatch_path = ~p"/channels/#{@channel}/subscription" unwatch_class = if @watching, do: "", else: "hidden" = if @conn.assigns.current_user do @@ -17,7 +17,7 @@ elixir: span.hidden--mobile ' Unsubscribe - else - a.media-box__header.media-box__header--channel.media-box__header--link href=Routes.session_path(@conn, :new) + a.media-box__header.media-box__header--channel.media-box__header--link href=~p"/sessions/new" i.fa.fa-bell> span.hidden--mobile ' Subscribe diff --git a/lib/philomena_web/templates/comment/_comment.html.slime b/lib/philomena_web/templates/comment/_comment.html.slime index 127ebe7cc..f3d40d6ad 100644 --- a/lib/philomena_web/templates/comment/_comment.html.slime +++ b/lib/philomena_web/templates/comment/_comment.html.slime @@ -12,7 +12,7 @@ article.block.communication id="comment_#{@comment.id}" class=block_class p ul.horizontal-list li - = link(to: Routes.image_comment_approve_path(@conn, :create, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "post", class: "button") do + = link(to: ~p"/images/#{@comment.image_id}/comments/#{@comment}/approve", data: [confirm: "Are you sure?"], method: "post", class: "button") do i.fas.fa-check> ' Approve li @@ -20,7 +20,7 @@ article.block.communication id="comment_#{@comment.id}" class=block_class i.fa.fa-times> ' Reject - = form_for :comment, Routes.image_comment_hide_path(@conn, :create, @comment.image_id, @comment), [class: "togglable-delete-form hidden flex", id: "inline-reject-form-comment-#{@comment.id}"], fn f -> + = form_for :comment, ~p"/images/#{@comment.image_id}/comments/#{@comment}/hide", [class: "togglable-delete-form hidden flex", id: "inline-reject-form-comment-#{@comment.id}"], fn f -> = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-reject-reason-comment-#{@comment.id}", required: true = submit "Delete", class: "button" @@ -38,12 +38,12 @@ article.block.communication id="comment_#{@comment.id}" class=block_class .communication__options__staff = cond do - @comment.hidden_from_users and not @comment.destroyed_content -> - = link(to: Routes.image_comment_hide_path(@conn, :delete, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "delete", class: "communication__interaction") do + = link(to: ~p"/images/#{@comment.image_id}/comments/#{@comment}/hide", data: [confirm: "Are you sure?"], method: "delete", class: "communication__interaction") do i.fas.fa-check> ' Restore = if can?(@conn, :delete, @comment) do - = link(to: Routes.image_comment_delete_path(@conn, :create, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "post", class: "communication__interaction") do + = link(to: ~p"/images/#{@comment.image_id}/comments/#{@comment}/delete", data: [confirm: "Are you sure?"], method: "post", class: "communication__interaction") do i.fas.fa-times> ' Delete Contents @@ -60,6 +60,6 @@ article.block.communication id="comment_#{@comment.id}" class=block_class .button.button--warning.button--transparent.js-staff-action =<> link_to_fingerprint(@conn, @comment.fingerprint) - = form_for :comment, Routes.image_comment_hide_path(@conn, :create, @comment.image_id, @comment), [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> + = form_for :comment, ~p"/images/#{@comment.image_id}/comments/#{@comment}/hide", [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-del-reason-comment-#{@comment.id}", required: true = submit "Delete", class: "button" diff --git a/lib/philomena_web/templates/comment/_comment_link.html.slime b/lib/philomena_web/templates/comment/_comment_link.html.slime index 862dc21d5..061e8c61b 100644 --- a/lib/philomena_web/templates/comment/_comment_link.html.slime +++ b/lib/philomena_web/templates/comment/_comment_link.html.slime @@ -1,10 +1,10 @@ -- comment_link = Routes.image_path(@conn, :show, @comment.image) <> "#comment_#{@comment.id}" +- comment_link = ~p"/images/#{@comment.image}" <> "#comment_#{@comment.id}" .flex__spacer.hidden--desktop a.communication__interaction.hidden--desktop title="Link to comment" href=comment_link i.fa.fa-link = if not is_nil(@comment.edited_at) and can?(@conn, :show, @comment) do - a.communication__options__edit-time href=Routes.image_comment_history_path(@conn, :index, @comment.image, @comment) + a.communication__options__edit-time href=~p"/images/#{@comment.image}/comments/#{@comment}/history" span.hidden--mobile> - edited span.hidden--desktop> Edited => pretty_time(@comment.edited_at) diff --git a/lib/philomena_web/templates/comment/_comment_options.html.slime b/lib/philomena_web/templates/comment/_comment_options.html.slime index b662b9820..5d1b7996c 100644 --- a/lib/philomena_web/templates/comment/_comment_options.html.slime +++ b/lib/philomena_web/templates/comment/_comment_options.html.slime @@ -1,11 +1,11 @@ = if not @comment.hidden_from_users or can?(@conn, :hide, @comment) do .flex.flex--normal-gap.flex--centered = if not @comment.hidden_from_users do - - link_path = Routes.image_path(@conn, :show, @comment.image) <> "#comment_#{@comment.id}" + - link_path = ~p"/images/#{@comment.image}" <> "#comment_#{@comment.id}" - safe_author = PhilomenaWeb.PostView.markdown_safe_author(@comment) - quote_body = if @comment.hidden_from_users, do: "", else: @comment.body - a.button href=Routes.image_comment_report_path(@conn, :new, @comment.image, @comment) + a.button href=~p"/images/#{@comment.image}/comments/#{@comment}/reports/new" i.fa.fa-flag> ' Report @@ -18,6 +18,6 @@ ' Reply = if not @comment.destroyed_content and can?(@conn, :edit, @comment) do - a.button href=Routes.image_comment_path(@conn, :edit, @comment.image, @comment) + a.button href=~p"/images/#{@comment.image}/comments/#{@comment}/edit" i.fas.fa-edit ' Edit diff --git a/lib/philomena_web/templates/comment/_comment_with_image.html.slime b/lib/philomena_web/templates/comment/_comment_with_image.html.slime index 1ff5e0ab9..60ab90dff 100644 --- a/lib/philomena_web/templates/comment/_comment_with_image.html.slime +++ b/lib/philomena_web/templates/comment/_comment_with_image.html.slime @@ -14,12 +14,12 @@ article.block.communication id="comment_#{@comment.id}" class=block_class .flex.flex--normal-gap.flex--centered.js-staff-action = cond do - @comment.hidden_from_users and not @comment.destroyed_content -> - = link(to: Routes.image_comment_hide_path(@conn, :delete, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "delete", class: "button button--success") do + = link(to: ~p"/images/#{@comment.image_id}/comments/#{@comment}/hide", data: [confirm: "Are you sure?"], method: "delete", class: "button button--success") do i.fas.fa-check> ' Restore = if can?(@conn, :delete, @comment) do - = link(to: Routes.image_comment_delete_path(@conn, :create, @comment.image_id, @comment), data: [confirm: "Are you sure?"], method: "post", class: "button button--danger button--transparent") do + = link(to: ~p"/images/#{@comment.image_id}/comments/#{@comment}/delete", data: [confirm: "Are you sure?"], method: "post", class: "button button--danger button--transparent") do i.fas.fa-times> ' Delete Contents @@ -36,6 +36,6 @@ article.block.communication id="comment_#{@comment.id}" class=block_class .button.button--warning.button--transparent.js-staff-action =<> link_to_fingerprint(@conn, @comment.fingerprint) - = form_for :comment, Routes.image_comment_hide_path(@conn, :create, @comment.image_id, @comment), [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> + = form_for :comment, ~p"/images/#{@comment.image_id}/comments/#{@comment}/hide", [class: "togglable-delete-form hidden flex", id: "inline-del-form-comment-#{@comment.id}"], fn f -> = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-del-reason-comment-#{@comment.id}", required: true = submit "Delete", class: "button" diff --git a/lib/philomena_web/templates/comment/index.html.slime b/lib/philomena_web/templates/comment/index.html.slime index 4494c793a..44aebd2d9 100644 --- a/lib/philomena_web/templates/comment/index.html.slime +++ b/lib/philomena_web/templates/comment/index.html.slime @@ -1,6 +1,6 @@ h1 Comments -= form_for :comments, Routes.comment_path(@conn, :index), [method: "get", class: "hform", enforce_utf8: false], fn f -> += form_for :comments, ~p"/comments", [method: "get", class: "hform", enforce_utf8: false], fn f -> .field = text_input f, :cq, name: :cq, value: @conn.params["cq"], class: "input hform__text", placeholder: "Search comments", autocapitalize: "none" = submit "Search", class: "hform__button button" @@ -14,7 +14,7 @@ h2 Search Results = cond do - Enum.any?(@comments) -> - - route = fn p -> Routes.comment_path(@conn, :index, p) end + - route = fn p -> ~p"/comments?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @comments, route: route, params: [cq: @conn.params["cq"]], conn: @conn = for {body, comment} <- @comments, comment.image.hidden_from_users == false do @@ -68,35 +68,35 @@ table.table td Literal td Matches the author of this comment. Anonymous authors will never match this term. td - code = link "author:Joey", to: Routes.comment_path(@conn, :index, cq: "author:Joey") + code = link "author:Joey", to: ~p"/comments?#{[cq: "author:Joey"]}" tr td code body td Full Text td Matches the body of this comment. This is the default field. td - code = link "body:test", to: Routes.comment_path(@conn, :index, cq: "body:test") + code = link "body:test", to: ~p"/comments?#{[cq: "body:test"]}" tr td code created_at td Date/Time Range td Matches the creation time of this comment. td - code = link "created_at:2015", to: Routes.comment_path(@conn, :index, cq: "created_at:2015") + code = link "created_at:2015", to: ~p"/comments?#{[cq: "created_at:2015"]}" tr td code id td Numeric Range td Matches the numeric surrogate key for this comment. td - code = link "id:1000000", to: Routes.comment_path(@conn, :index, cq: "id:1000000") + code = link "id:1000000", to: ~p"/comments?#{[cq: "id:1000000"]}" tr td code image_id td Literal td Matches the numeric surrogate key for the image this comment belongs to. td - code = link "image_id:1000000", to: Routes.comment_path(@conn, :index, cq: "image_id:1000000") + code = link "image_id:1000000", to: ~p"/comments?#{[cq: "image_id:1000000"]}" tr td code my @@ -105,11 +105,11 @@ table.table code> my:comments ' matches comments you have posted if you are signed in. td - code = link "my:comments", to: Routes.comment_path(@conn, :index, cq: "my:comments") + code = link "my:comments", to: ~p"/comments?#{[cq: "my:comments"]}" tr td code user_id td Literal td Matches comments with the specified user_id. Anonymous users will never match this term. td - code = link "user_id:211190", to: Routes.comment_path(@conn, :index, cq: "user_id:211190") + code = link "user_id:211190", to: ~p"/comments?#{[cq: "user_id:211190"]}" diff --git a/lib/philomena_web/templates/commission/_directory_results.html.slime b/lib/philomena_web/templates/commission/_directory_results.html.slime index aa2a70013..8071b52fe 100644 --- a/lib/philomena_web/templates/commission/_directory_results.html.slime +++ b/lib/philomena_web/templates/commission/_directory_results.html.slime @@ -1,5 +1,5 @@ elixir: - route = fn p -> Routes.commission_path(@conn, :index, p) end + route = fn p -> ~p"/commissions?#{p}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @commissions, route: route, conn: @conn, params: [commission: @conn.params["commission"]] .block @@ -9,10 +9,10 @@ elixir: .page__info = cond do - not is_nil(@conn.assigns.current_user) and not is_nil(@conn.assigns.current_user.commission) -> - = link "View my listing", to: Routes.profile_commission_path(@conn, :show, @conn.assigns.current_user) + = link "View my listing", to: ~p"/profiles/#{@conn.assigns.current_user}/commission" - not is_nil(@conn.assigns.current_user) -> - = link "Create my listing", to: Routes.profile_commission_path(@conn, :new, @conn.assigns.current_user) + = link "Create my listing", to: ~p"/profiles/#{@conn.assigns.current_user}/commission/new" - true -> @@ -58,7 +58,7 @@ elixir: p strong - = link "More information", to: Routes.profile_commission_path(@conn, :show, c.user) + = link "More information", to: ~p"/profiles/#{c.user}/commission" - true -> p We couldn't find any commission listings to display. Sorry! diff --git a/lib/philomena_web/templates/commission/_directory_sidebar.html.slime b/lib/philomena_web/templates/commission/_directory_sidebar.html.slime index f9f8670aa..7693e8b93 100644 --- a/lib/philomena_web/templates/commission/_directory_sidebar.html.slime +++ b/lib/philomena_web/templates/commission/_directory_sidebar.html.slime @@ -2,7 +2,7 @@ .block__header span Search .block__content - = form_for @conn, Routes.commission_path(@conn, :index), [as: :commission, method: "get", class: "hform"], fn f -> + = form_for @conn, ~p"/commissions", [as: :commission, method: "get", class: "hform"], fn f -> .field = label f, :categories, "Art Categories:" = for {name, value} <- categories() do diff --git a/lib/philomena_web/templates/confirmation/new.html.slime b/lib/philomena_web/templates/confirmation/new.html.slime index 2f4a5177b..7972f5951 100644 --- a/lib/philomena_web/templates/confirmation/new.html.slime +++ b/lib/philomena_web/templates/confirmation/new.html.slime @@ -1,6 +1,6 @@ h1 Resend confirmation instructions -= form_for :user, Routes.confirmation_path(@conn, :create), fn f -> += form_for :user, ~p"/confirmations", fn f -> .field = email_input f, :email, placeholder: "Email", class: "input", required: true diff --git a/lib/philomena_web/templates/conversation/index.html.slime b/lib/philomena_web/templates/conversation/index.html.slime index 6abf46a90..4528329bc 100644 --- a/lib/philomena_web/templates/conversation/index.html.slime +++ b/lib/philomena_web/templates/conversation/index.html.slime @@ -1,5 +1,5 @@ elixir: - route = fn p -> Routes.conversation_path(@conn, :index, p) end + route = fn p -> ~p"/conversations?#{p}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @conversations, route: route, conn: @conn h1 My Conversations @@ -8,7 +8,7 @@ h1 My Conversations .page__pagination = pagination .page__info - a href=Routes.conversation_path(@conn, :new) + a href=~p"/conversations/new" i.fa.fa-paper-plane> ' Create New Conversation @@ -23,7 +23,7 @@ h1 My Conversations = for {c, count} <- @conversations do tr class=conversation_class(@conn.assigns.current_user, c) td.table--communication-list__name - => link c.title, to: Routes.conversation_path(@conn, :show, c) + => link c.title, to: ~p"/conversations/#{c}" .small-text.hidden--mobile => count @@ -36,9 +36,9 @@ h1 My Conversations td.table--communication-list__stats = render PhilomenaWeb.UserAttributionView, "_user.html", object: %{user: other_party(@current_user, c)}, conn: @conn td.table--communication-list__options - => link "Last message", to: last_message_path(@conn, c, count) + => link "Last message", to: last_message_path(c, count) ' • - => link "Hide", to: Routes.conversation_hide_path(@conn, :create, c), data: [method: "post"], data: [confirm: "Are you really, really sure?"] + => link "Hide", to: ~p"/conversations/#{c}/hide", data: [method: "post"], data: [confirm: "Are you really, really sure?"] .block__header.block__header--light.page__header .page__pagination = pagination diff --git a/lib/philomena_web/templates/conversation/message/_form.html.slime b/lib/philomena_web/templates/conversation/message/_form.html.slime index 7234a3e76..cbaec375f 100644 --- a/lib/philomena_web/templates/conversation/message/_form.html.slime +++ b/lib/philomena_web/templates/conversation/message/_form.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.conversation_message_path(@conn, :create, @conversation), fn f -> += form_for @changeset, ~p"/conversations/#{@conversation}/messages", fn f -> .block .communication-edit__wrap = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, action_icon: "pencil-alt", action_text: "Reply" diff --git a/lib/philomena_web/templates/conversation/new.html.slime b/lib/philomena_web/templates/conversation/new.html.slime index 31207ac83..ef0f3758a 100644 --- a/lib/philomena_web/templates/conversation/new.html.slime +++ b/lib/philomena_web/templates/conversation/new.html.slime @@ -1,7 +1,7 @@ h1 New Conversation .block .block__header - => link "Conversations", to: Routes.conversation_path(@conn, :index) + => link "Conversations", to: ~p"/conversations" ' » span New Conversation @@ -20,7 +20,7 @@ h1 New Conversation - _ -> / Nothing -= form_for @changeset, Routes.conversation_path(@conn, :create), fn f -> += form_for @changeset, ~p"/conversations", fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/conversation/show.html.slime b/lib/philomena_web/templates/conversation/show.html.slime index 537533c17..e62d6fc47 100644 --- a/lib/philomena_web/templates/conversation/show.html.slime +++ b/lib/philomena_web/templates/conversation/show.html.slime @@ -1,14 +1,14 @@ elixir: - route = fn p -> Routes.conversation_path(@conn, :show, @conversation, p) end + route = fn p -> ~p"/conversations/#{@conversation}?#{p}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @messages, route: route, conn: @conn other = other_party(@current_user, @conversation) h1 = @conversation.title .block .block__header - => link "Message Center", to: Routes.conversation_path(@conn, :index) + => link "Message Center", to: ~p"/conversations" ' » - => link @conversation.title, to: Routes.conversation_path(@conn, :show, @conversation) + => link @conversation.title, to: ~p"/conversations/#{@conversation}" ' Conversation with => render PhilomenaWeb.UserAttributionView, "_user.html", object: %{user: other}, conn: @conn .block__header--sub.block__header--light.page__header @@ -18,11 +18,11 @@ h1 = @conversation.title .page__pagination = pagination .page__info = if hidden_by?(@current_user, @conversation) do - = link "Restore conversation", to: Routes.conversation_hide_path(@conn, :delete, @conversation), data: [method: "delete"] + = link "Restore conversation", to: ~p"/conversations/#{@conversation}/hide", data: [method: "delete"] - else - = link "Remove conversation", to: Routes.conversation_hide_path(@conn, :create, @conversation), data: [method: "post", confirm: "Are you really, really sure?"] - = link "Report conversation", to: Routes.conversation_report_path(@conn, :new, @conversation) - = link "Mark as unread", to: Routes.conversation_read_path(@conn, :delete, @conversation), data: [method: "delete"] + = link "Remove conversation", to: ~p"/conversations/#{@conversation}/hide", data: [method: "post", confirm: "Are you really, really sure?"] + = link "Report conversation", to: ~p"/conversations/#{@conversation}/reports/new" + = link "Mark as unread", to: ~p"/conversations/#{@conversation}/read", data: [method: "delete"] = for {message, body} <- @messages do = render PhilomenaWeb.MessageView, "_message.html", message: message, body: body, conn: @conn @@ -59,5 +59,5 @@ h1 = @conversation.title p You've managed to send over 1,000 messages in this conversation! p We'd like to ask you to make a new conversation. Don't worry, this one won't go anywhere if you need to refer back to it. p - => link "Click here", to: Routes.conversation_path(@conn, :new, receipient: other.name) + => link "Click here", to: ~p"/conversations/new?#{[receipient: other.name]}" ' to make a new conversation with this user. diff --git a/lib/philomena_web/templates/dnp_entry/edit.html.slime b/lib/philomena_web/templates/dnp_entry/edit.html.slime index 2761b3953..969f4ffdc 100644 --- a/lib/philomena_web/templates/dnp_entry/edit.html.slime +++ b/lib/philomena_web/templates/dnp_entry/edit.html.slime @@ -1,2 +1,2 @@ h2 Edit DNP Request -= render PhilomenaWeb.DnpEntryView, "_form.html", changeset: @changeset, action: Routes.dnp_entry_path(@conn, :update, @dnp_entry, tag_id: @dnp_entry.tag_id), conn: @conn, selectable_tags: @selectable_tags += render PhilomenaWeb.DnpEntryView, "_form.html", changeset: @changeset, action: ~p"/dnp/#{@dnp_entry}?#{[tag_id: @dnp_entry.tag_id]}", conn: @conn, selectable_tags: @selectable_tags diff --git a/lib/philomena_web/templates/dnp_entry/index.html.slime b/lib/philomena_web/templates/dnp_entry/index.html.slime index 634150386..3a51abce8 100644 --- a/lib/philomena_web/templates/dnp_entry/index.html.slime +++ b/lib/philomena_web/templates/dnp_entry/index.html.slime @@ -16,13 +16,13 @@ br .block__content = cond do - not is_nil(@current_user) and Enum.any?(@linked_tags) -> - = link "Create an entry", to: Routes.dnp_entry_path(@conn, :new) + = link "Create an entry", to: ~p"/dnp/new" br - = link "My Listings", to: Routes.dnp_entry_path(@conn, :index, mine: "1") + = link "My Listings", to: ~p"/dnp?#{[mine: "1"]}" - not is_nil(@current_user) -> ' You must have a verified artist link to create and manage DNP entries. - = link "Request an artist link", to: Routes.profile_artist_link_path(@conn, :new, @current_user) + = link "Request an artist link", to: ~p"/profiles/#{@current_user}/artist_links/new" | . - true -> @@ -31,7 +31,7 @@ br h3 The List .block - - route = fn p -> Routes.dnp_entry_path(@conn, :index, p) end + - route = fn p -> ~p"/dnp?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @dnp_entries, route: route, conn: @conn .block__header.page__header @@ -69,7 +69,7 @@ h3 The List = pretty_time(entry.created_at) td - = link "More Info", to: Routes.dnp_entry_path(@conn, :show, entry) + = link "More Info", to: ~p"/dnp/#{entry}" .block__header.page__header .page__pagination = pagination diff --git a/lib/philomena_web/templates/dnp_entry/new.html.slime b/lib/philomena_web/templates/dnp_entry/new.html.slime index 95c822d27..1bb616f8c 100644 --- a/lib/philomena_web/templates/dnp_entry/new.html.slime +++ b/lib/philomena_web/templates/dnp_entry/new.html.slime @@ -1,2 +1,2 @@ h2 New DNP Request -= render PhilomenaWeb.DnpEntryView, "_form.html", changeset: @changeset, action: Routes.dnp_entry_path(@conn, :create, tag_id: @conn.params["tag_id"]), conn: @conn, selectable_tags: @selectable_tags += render PhilomenaWeb.DnpEntryView, "_form.html", changeset: @changeset, action: ~p"/dnp?#{[tag_id: @conn.params["tag_id"]]}", conn: @conn, selectable_tags: @selectable_tags diff --git a/lib/philomena_web/templates/dnp_entry/show.html.slime b/lib/philomena_web/templates/dnp_entry/show.html.slime index a7342f4db..34184dd1b 100644 --- a/lib/philomena_web/templates/dnp_entry/show.html.slime +++ b/lib/philomena_web/templates/dnp_entry/show.html.slime @@ -6,9 +6,9 @@ h2 .block__header span DNP Information = if can?(@conn, :edit, @dnp_entry) do - = link "Edit listing", to: Routes.dnp_entry_path(@conn, :edit, @dnp_entry, tag_id: @dnp_entry.tag_id) + = link "Edit listing", to: ~p"/dnp/#{@dnp_entry}/edit?#{[tag_id: @dnp_entry.tag_id]}" - = link "Back to DNP List", to: Routes.dnp_entry_path(@conn, :index) + = link "Back to DNP List", to: ~p"/dnp" .block__content table.table @@ -53,26 +53,26 @@ h2 = if can?(@conn, :index, Philomena.DnpEntries.DnpEntry) do = case @dnp_entry.aasm_state do - s when s in ["requested", "claimed"] -> - => link "Claim", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "claimed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Claim", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "claimed"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - => link "Approve", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "listed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Approve", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "listed"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - => link "Close", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "closed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Close", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "closed"]}", data: [method: "post", confirm: "Are you really, really sure?"] - "listed" -> - => link "Rescind", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "rescinded"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Rescind", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "rescinded"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - = link "Close", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "closed"), data: [method: "post", confirm: "Are you really, really sure?"] + = link "Close", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "closed"]}", data: [method: "post", confirm: "Are you really, really sure?"] - s when s in ["rescinded", "acknowledged"] -> - => link "Claim", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "acknowledged"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Claim", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "acknowledged"]}", data: [method: "post", confirm: "Are you really, really sure?"] ' • - = link "Close", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "closed"), data: [method: "post", confirm: "Are you really, really sure?"] + = link "Close", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "closed"]}", data: [method: "post", confirm: "Are you really, really sure?"] - _state -> - => link "Claim", to: Routes.admin_dnp_entry_transition_path(@conn, :create, @dnp_entry, state: "claimed"), data: [method: "post", confirm: "Are you really, really sure?"] + => link "Claim", to: ~p"/admin/dnp_entries/#{@dnp_entry}/transition?#{[state: "claimed"]}", data: [method: "post", confirm: "Are you really, really sure?"] = if assigns[:mod_notes] do h4 Mod Notes = render PhilomenaWeb.Admin.ModNoteView, "_table.html", mod_notes: @mod_notes, conn: @conn - = link "Add New Note", to: Routes.admin_mod_note_path(@conn, :new, notable_id: @dnp_entry.id, notable_type: "DnpEntry") + = link "Add New Note", to: ~p"/admin/mod_notes/new?#{[notable_id: @dnp_entry.id, notable_type: "DnpEntry"]}" diff --git a/lib/philomena_web/templates/duplicate_report/_form.html.slime b/lib/philomena_web/templates/duplicate_report/_form.html.slime index 128de57b8..19d1ec648 100644 --- a/lib/philomena_web/templates/duplicate_report/_form.html.slime +++ b/lib/philomena_web/templates/duplicate_report/_form.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.duplicate_report_path(@conn, :create), fn f -> += form_for @changeset, ~p"/duplicate_reports", fn f -> = hidden_input f, :image_id, value: @image.id .field ' Delete this image and redirect to diff --git a/lib/philomena_web/templates/duplicate_report/_image_cell.html.slime b/lib/philomena_web/templates/duplicate_report/_image_cell.html.slime index 1e262a350..9e52462c4 100644 --- a/lib/philomena_web/templates/duplicate_report/_image_cell.html.slime +++ b/lib/philomena_web/templates/duplicate_report/_image_cell.html.slime @@ -18,12 +18,12 @@ = if can?(@conn, :edit, @report) and mergeable?(@report) do = if @source do - a href=Routes.duplicate_report_accept_reverse_path(@conn, :create, @report) data-method="post" + a href=~p"/duplicate_reports/#{@report}/accept_reverse" data-method="post" button.button ' Keep Source i.fa.fa-arrow-left - else - a href=Routes.duplicate_report_accept_path(@conn, :create, @report) data-method="post" + a href=~p"/duplicate_reports/#{@report}/accept" data-method="post" button.button i.fa.fa-arrow-right> | Keep Target diff --git a/lib/philomena_web/templates/duplicate_report/_list.html.slime b/lib/philomena_web/templates/duplicate_report/_list.html.slime index 87de9fc0a..bb18bb509 100644 --- a/lib/philomena_web/templates/duplicate_report/_list.html.slime +++ b/lib/philomena_web/templates/duplicate_report/_list.html.slime @@ -15,7 +15,7 @@ tr = if same_aspect_ratio?(report) do td.success - a href=Routes.duplicate_report_path(@conn, :show, report) + a href=~p"/duplicate_reports/#{report}" ' Visual diff | (Same aspect ratio) @@ -139,13 +139,13 @@ = if can?(@conn, :edit, report) do div = if report.state == "open" do - a href=(Routes.duplicate_report_claim_path(@conn, :create, report) <> "#report_options_#{report.id}") data-method="post" + a href=(~p"/duplicate_reports/#{report}/claim" <> "#report_options_#{report.id}") data-method="post" button.button.button--separate-right i.fa.fa-clipboard> ' Claim = if report.state != "rejected" do - a href=Routes.duplicate_report_reject_path(@conn, :create, report) data-method="post" + a href=~p"/duplicate_reports/#{report}/reject" data-method="post" button.button i.fa.fa-times> ' Reject @@ -157,6 +157,6 @@ = if report.user do ' by - =< link report.user.name, to: Routes.profile_path(@conn, :show, report.user) + =< link report.user.name, to: ~p"/profiles/#{report.user}" = escape_nl2br(report.reason) diff --git a/lib/philomena_web/templates/duplicate_report/index.html.slime b/lib/philomena_web/templates/duplicate_report/index.html.slime index 0837f277c..a76e986aa 100644 --- a/lib/philomena_web/templates/duplicate_report/index.html.slime +++ b/lib/philomena_web/templates/duplicate_report/index.html.slime @@ -1,6 +1,6 @@ h1 Duplicate Reports -- route = fn p -> Routes.duplicate_report_path(@conn, :index, p) end +- route = fn p -> ~p"/duplicate_reports?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @duplicate_reports, route: route, conn: @conn, params: [states: @conn.params["states"] || ["open", "claimed"]] .block @@ -8,14 +8,14 @@ h1 Duplicate Reports .page__pagination = pagination .page__info span Display only: - => link "Open (All)", to: Routes.duplicate_report_path(@conn, :index, states: ~W(open claimed)) - => link "Open (Unclaimed)", to: Routes.duplicate_report_path(@conn, :index, states: ~W(open)) - => link "Open (Claimed)", to: Routes.duplicate_report_path(@conn, :index, states: ~W(claimed)) - => link "Open + Rejected", to: Routes.duplicate_report_path(@conn, :index, states: ~W(open rejected)) - => link "Rejected", to: Routes.duplicate_report_path(@conn, :index, states: ~W(rejected)) - => link "Rejected + Accepted", to: Routes.duplicate_report_path(@conn, :index, states: ~W(rejected accepted)) - => link "Accepted", to: Routes.duplicate_report_path(@conn, :index, states: ~W(accepted)) - = link "All", to: Routes.duplicate_report_path(@conn, :index, states: ~W(open rejected accepted claimed)) + => link "Open (All)", to: ~p"/duplicate_reports?#{[states: ~W(open claimed)]}" + => link "Open (Unclaimed)", to: ~p"/duplicate_reports?#{[states: ~W(open)]}" + => link "Open (Claimed)", to: ~p"/duplicate_reports?#{[states: ~W(claimed)]}" + => link "Open + Rejected", to: ~p"/duplicate_reports?#{[states: ~W(open rejected)]}" + => link "Rejected", to: ~p"/duplicate_reports?#{[states: ~W(rejected)]}" + => link "Rejected + Accepted", to: ~p"/duplicate_reports?#{[states: ~W(rejected accepted)]}" + => link "Accepted", to: ~p"/duplicate_reports?#{[states: ~W(accepted)]}" + = link "All", to: ~p"/duplicate_reports?#{[states: ~W(open rejected accepted claimed)]}" = render PhilomenaWeb.DuplicateReportView, "_list.html", duplicate_reports: @duplicate_reports, conn: @conn diff --git a/lib/philomena_web/templates/filter/_filter.html.slime b/lib/philomena_web/templates/filter/_filter.html.slime index 859608f7d..ab4e92604 100644 --- a/lib/philomena_web/templates/filter/_filter.html.slime +++ b/lib/philomena_web/templates/filter/_filter.html.slime @@ -20,20 +20,20 @@ = length(@filter.hidden_tag_ids) li - = link "View this filter", to: Routes.filter_path(@conn, :show, @filter), class: "button" + = link "View this filter", to: ~p"/filters/#{@filter}", class: "button" li - = link "Copy and Customize", to: Routes.filter_path(@conn, :new, based_on: @filter), class: "button" + = link "Copy and Customize", to: ~p"/filters/new?#{[based_on: @filter]}", class: "button" = if can?(@conn, :edit, @filter) do li - = link "Edit this filter", to: Routes.filter_path(@conn, :edit, @filter), class: "button" + = link "Edit this filter", to: ~p"/filters/#{@filter}/edit", class: "button" = if @filter.id == @conn.assigns.current_filter.id do li strong Your current filter - else li - = button_to "Use this filter", Routes.filter_current_path(@conn, :update, id: @filter), method: "put", class: "button" + = button_to "Use this filter", ~p"/filters/current?#{[id: @filter]}", method: "put", class: "button" p em = @filter.description diff --git a/lib/philomena_web/templates/filter/edit.html.slime b/lib/philomena_web/templates/filter/edit.html.slime index 4ec942530..db0bf5d6b 100644 --- a/lib/philomena_web/templates/filter/edit.html.slime +++ b/lib/philomena_web/templates/filter/edit.html.slime @@ -1,5 +1,5 @@ h2 Editing Filter -= render PhilomenaWeb.FilterView, "_form.html", filter: @changeset, route: Routes.filter_path(@conn, :update, @filter) += render PhilomenaWeb.FilterView, "_form.html", filter: @changeset, route: ~p"/filters/#{@filter}" = if not @filter.public and not @filter.system do br @@ -19,4 +19,4 @@ h2 Editing Filter strong irreversible ' . Once you make a filter public, you cannot make it private again. - = button_to "Convert to Public Filter", Routes.filter_public_path(@conn, :create, @filter), class: "button", method: "create", data: [confirm: "Are you really, really sure?"] + = button_to "Convert to Public Filter", ~p"/filters/#{@filter}/public", class: "button", method: "create", data: [confirm: "Are you really, really sure?"] diff --git a/lib/philomena_web/templates/filter/index.html.slime b/lib/philomena_web/templates/filter/index.html.slime index 3056bd149..8b94a091d 100644 --- a/lib/philomena_web/templates/filter/index.html.slime +++ b/lib/philomena_web/templates/filter/index.html.slime @@ -28,7 +28,7 @@ h2 My Filters = if @current_user do p - = link("Click here to make a new filter from scratch", to: Routes.filter_path(@conn, :new)) + = link("Click here to make a new filter from scratch", to: ~p"/filters/new") = for filter <- @my_filters do = render PhilomenaWeb.FilterView, "_filter.html", conn: @conn, filter: filter - else @@ -43,12 +43,12 @@ h2 Recent Filters p ' Clicking this button will clear the recent filters list in the header dropdown. - = button_to "Clear recent filter list", Routes.filter_clear_recent_path(@conn, :delete), method: "delete", class: "button" + = button_to "Clear recent filter list", ~p"/filters/clear_recent", method: "delete", class: "button" h2 Search Filters p ' Some users maintain custom filters which are publicly shared; you can search these filters with the box below. - = form_for :filters, Routes.filter_path(@conn, :index), [method: "get", class: "hform", enforce_utf8: false], fn f -> + = form_for :filters, ~p"/filters", [method: "get", class: "hform", enforce_utf8: false], fn f -> .field = text_input f, :fq, name: :fq, value: @conn.params["fq"], class: "input hform__text", placeholder: "Search filters", autocapitalize: "none" = submit "Search", class: "hform__button button" @@ -62,7 +62,7 @@ h2 Search Results = cond do - Enum.any?(@filters) -> - - route = fn p -> Routes.filter_path(@conn, :index, p) end + - route = fn p -> ~p"/filters?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @filters, route: route, params: [fq: @conn.params["fq"]], conn: @conn = for filter <- @filters do @@ -99,49 +99,49 @@ table.table td Literal td Matches the creator of this filter. td - code = link "creator:AppleDash", to: Routes.filter_path(@conn, :index, fq: "creator:AppleDash") + code = link "creator:AppleDash", to: ~p"/filters?#{[fq: "creator:AppleDash"]}" tr td code name td Literal td Matches the name of this filter. This is the default field. td - code = link "name:default", to: Routes.filter_path(@conn, :index, fq: "name:default") + code = link "name:default", to: ~p"/filters?#{[fq: "name:default"]}" tr td code description td Full Text td Matches the description of this filter. td - code = link "description:the show's rating", to: Routes.filter_path(@conn, :index, fq: "description:the show's rating") + code = link "description:the show's rating", to: ~p"/filters?#{[fq: "description:the show's rating"]}" tr td code created_at td Date/Time Range td Matches the creation time of this filter. td - code = link "created_at:2015", to: Routes.filter_path(@conn, :index, fq: "created_at:2015") + code = link "created_at:2015", to: ~p"/filters?#{[fq: "created_at:2015"]}" tr td code id td Numeric Range td Matches the numeric surrogate key for this filter. td - code = link "id:1", to: Routes.filter_path(@conn, :index, fq: "id:1") + code = link "id:1", to: ~p"/filters?#{[fq: "id:1"]}" tr td code spoilered_count td Numeric Range td Matches the number of spoilered tags in this filter. td - code = link "spoilered_count:1", to: Routes.filter_path(@conn, :index, fq: "spoilered_count:1") + code = link "spoilered_count:1", to: ~p"/filters?#{[fq: "spoilered_count:1"]}" tr td code hidden_count td Numeric Range td Matches the number of hidden tags in this filter. td - code = link "hidden_count:1", to: Routes.filter_path(@conn, :index, fq: "hidden_count:1") + code = link "hidden_count:1", to: ~p"/filters?#{[fq: "hidden_count:1"]}" tr td code my @@ -150,14 +150,14 @@ table.table code> my:filters ' matches filters you have published if you are signed in. td - code = link "my:filters", to: Routes.filter_path(@conn, :index, fq: "my:filters") + code = link "my:filters", to: ~p"/filters?#{[fq: "my:filters"]}" tr td code system td Boolean td Matches system filters td - code = link "system:true", to: Routes.filter_path(@conn, :index, fq: "system:true") + code = link "system:true", to: ~p"/filters?#{[fq: "system:true"]}" tr td code public @@ -167,14 +167,14 @@ table.table code> public:false ' matches only your own private filters. td - code = link "public:false", to: Routes.filter_path(@conn, :index, fq: "public:false") + code = link "public:false", to: ~p"/filters?#{[fq: "public:false"]}" tr td code user_id td Literal td Matches filters with the specified user_id. td - code = link "user_id:307505", to: Routes.filter_path(@conn, :index, fq: "user_id:307505") + code = link "user_id:307505", to: ~p"/filters?#{[fq: "user_id:307505"]}" = if @conn.params["fq"] do - p = link("Back to filters", to: Routes.filter_path(@conn, :index)) + p = link("Back to filters", to: ~p"/filters") diff --git a/lib/philomena_web/templates/filter/new.html.slime b/lib/philomena_web/templates/filter/new.html.slime index de8810736..26dc5513d 100644 --- a/lib/philomena_web/templates/filter/new.html.slime +++ b/lib/philomena_web/templates/filter/new.html.slime @@ -1,2 +1,2 @@ h2 Creating New Filter -= render PhilomenaWeb.FilterView, "_form.html", filter: @changeset, route: Routes.filter_path(@conn, :create) \ No newline at end of file += render PhilomenaWeb.FilterView, "_form.html", filter: @changeset, route: ~p"/filters" \ No newline at end of file diff --git a/lib/philomena_web/templates/filter/show.html.slime b/lib/philomena_web/templates/filter/show.html.slime index 156084828..6e1221f31 100644 --- a/lib/philomena_web/templates/filter/show.html.slime +++ b/lib/philomena_web/templates/filter/show.html.slime @@ -18,14 +18,14 @@ h1 strong Your current filter - else li - = button_to "Use this filter", Routes.filter_current_path(@conn, :update, id: @filter), method: "put", class: "button" + = button_to "Use this filter", ~p"/filters/current?#{[id: @filter]}", method: "put", class: "button" = if can?(@conn, :edit, @filter) do li - = link "Edit this filter", to: Routes.filter_path(@conn, :edit, @filter), class: "button" + = link "Edit this filter", to: ~p"/filters/#{@filter}/edit", class: "button" = if can?(@conn, :delete, @filter) do - = button_to "Destroy this filter", Routes.filter_path(@conn, :delete, @filter), method: "delete", class: "button", data: [confirm: "Are you really, really sure?"] + = button_to "Destroy this filter", ~p"/filters/#{@filter}", method: "delete", class: "button", data: [confirm: "Are you really, really sure?"] = if @filter.user do p.filter-maintainer @@ -61,4 +61,4 @@ h1 /p = link("Report filter to moderators", new_report_path(reportable_class: 'filter', reportable_id: @filter.id) -p = link("Back to filters", to: Routes.filter_path(@conn, :index)) +p = link("Back to filters", to: ~p"/filters") diff --git a/lib/philomena_web/templates/fingerprint_profile/show.html.slime b/lib/philomena_web/templates/fingerprint_profile/show.html.slime index dae6b6a27..cfb36e1e2 100644 --- a/lib/philomena_web/templates/fingerprint_profile/show.html.slime +++ b/lib/philomena_web/templates/fingerprint_profile/show.html.slime @@ -3,24 +3,24 @@ h1 ' 's fingerprint profile ul - li = link "View images this fingerprint has uploaded", to: Routes.search_path(@conn, :index, q: "fingerprint:#{@fingerprint}") - li = link "View comments this fingerprint has posted", to: Routes.comment_path(@conn, :index, cq: "fingerprint:#{@fingerprint}") - li = link "View posts this fingerprint has made", to: Routes.post_path(@conn, :index, pq: "fingerprint:#{@fingerprint}") + li = link "View images this fingerprint has uploaded", to: ~p"/search?#{[q: "fingerprint:#{@fingerprint}"]}" + li = link "View comments this fingerprint has posted", to: ~p"/comments?#{[cq: "fingerprint:#{@fingerprint}"]}" + li = link "View posts this fingerprint has made", to: ~p"/posts?#{[pq: "fingerprint:#{@fingerprint}"]}" = render PhilomenaWeb.BanView, "_bans.html", bans: @fingerprint_bans, conn: @conn h2 Administration Options ul - li = link "View tag changes", to: Routes.fingerprint_profile_tag_change_path(@conn, :index, @fingerprint) - li = link "View source URL history", to: Routes.fingerprint_profile_source_change_path(@conn, :index, @fingerprint) - li = link "View reports this fingerprint has made", to: Routes.admin_report_path(@conn, :index, rq: "fingerprint:#{@fingerprint}") - li = link "View fingerprint ban history", to: Routes.admin_fingerprint_ban_path(@conn, :index, fingerprint: @fingerprint) - li = link "Ban this sucker", to: Routes.admin_fingerprint_ban_path(@conn, :new, fingerprint: @fingerprint) + li = link "View tag changes", to: ~p"/fingerprint_profiles/#{@fingerprint}/tag_changes" + li = link "View source URL history", to: ~p"/fingerprint_profiles/#{@fingerprint}/source_changes" + li = link "View reports this fingerprint has made", to: ~p"/admin/reports?#{[rq: "fingerprint:#{@fingerprint}"]}" + li = link "View fingerprint ban history", to: ~p"/admin/fingerprint_bans?#{[fingerprint: @fingerprint]}" + li = link "Ban this sucker", to: ~p"/admin/fingerprint_bans/new?#{[fingerprint: @fingerprint]}" h2 Actions ul - li = link "Revert all tag changes", to: Routes.tag_change_full_revert_path(@conn, :create, [fingerprint: @fingerprint]), data: [confirm: "Are you really, really sure?", method: "create"] - li = link "...the button above didn't work (use carefully, this is resource-intensive)", to: Routes.tag_change_full_revert_path(@conn, :create, [fingerprint: @fingerprint, batch_size: 1]), data: [confirm: "Please confirm that you're aware that this may crash the site and are ready to take on the full wrath of the admins if it does so after you press this button.", method: "create"] + li = link "Revert all tag changes", to: ~p"/tag_changes/full_revert?#{[fingerprint: @fingerprint]}", data: [confirm: "Are you really, really sure?", method: "create"] + li = link "...the button above didn't work (use carefully, this is resource-intensive)", to: ~p"/tag_changes/full_revert?#{[fingerprint: @fingerprint, batch_size: 1]}", data: [confirm: "Please confirm that you're aware that this may crash the site and are ready to take on the full wrath of the admins if it does so after you press this button.", method: "create"] h4 Observed users table.table @@ -34,7 +34,7 @@ table.table = for ufp <- @user_fps do tr td - = link ufp.user.name, to: Routes.profile_path(@conn, :show, ufp.user) + = link ufp.user.name, to: ~p"/profiles/#{ufp.user}" td => ufp.uses ' times diff --git a/lib/philomena_web/templates/fingerprint_profile/source_change/index.html.slime b/lib/philomena_web/templates/fingerprint_profile/source_change/index.html.slime index d1dd14401..0c92e88d8 100644 --- a/lib/philomena_web/templates/fingerprint_profile/source_change/index.html.slime +++ b/lib/philomena_web/templates/fingerprint_profile/source_change/index.html.slime @@ -2,7 +2,7 @@ h1 ' Source changes by = @fingerprint -- route = fn p -> Routes.fingerprint_profile_source_change_path(@conn, :index, @fingerprint, p) end +- route = fn p -> ~p"/fingerprint_profiles/#{@fingerprint}/source_changes?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @source_changes, route: route, conn: @conn = render PhilomenaWeb.SourceChangeView, "index.html", conn: @conn, source_changes: @source_changes, pagination: pagination diff --git a/lib/philomena_web/templates/fingerprint_profile/tag_change/index.html.slime b/lib/philomena_web/templates/fingerprint_profile/tag_change/index.html.slime index 5beca37dd..492f95eee 100644 --- a/lib/philomena_web/templates/fingerprint_profile/tag_change/index.html.slime +++ b/lib/philomena_web/templates/fingerprint_profile/tag_change/index.html.slime @@ -2,7 +2,7 @@ h1 ' Tag changes by = @fingerprint -- route = fn p -> Routes.fingerprint_profile_tag_change_path(@conn, :index, @fingerprint, p) end +- route = fn p -> ~p"/fingerprint_profiles/#{@fingerprint}/tag_changes?#{p}" end - params = if @conn.params["added"], do: [added: @conn.params["added"]] - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @tag_changes, route: route, conn: @conn, params: params @@ -11,8 +11,8 @@ h1 span | Display only: - = link "Removed", to: Routes.fingerprint_profile_tag_change_path(@conn, :index, @fingerprint, added: 0) - = link "Added", to: Routes.fingerprint_profile_tag_change_path(@conn, :index, @fingerprint, added: 1) - = link "All", to: Routes.fingerprint_profile_tag_change_path(@conn, :index, @fingerprint) + = link "Removed", to: ~p"/fingerprint_profiles/#{@fingerprint}/tag_changes?#{[added: 0]}" + = link "Added", to: ~p"/fingerprint_profiles/#{@fingerprint}/tag_changes?#{[added: 1]}" + = link "All", to: ~p"/fingerprint_profiles/#{@fingerprint}/tag_changes" = render PhilomenaWeb.TagChangeView, "index.html", conn: @conn, tag_changes: @tag_changes, pagination: pagination diff --git a/lib/philomena_web/templates/forum/index.html.slime b/lib/philomena_web/templates/forum/index.html.slime index 5d2035044..df328dee0 100644 --- a/lib/philomena_web/templates/forum/index.html.slime +++ b/lib/philomena_web/templates/forum/index.html.slime @@ -1,7 +1,7 @@ h1 Discussion Forums .block .block__header - a href=Routes.post_path(@conn, :index) + a href=~p"/posts" i.fa.icon--padded.small.fa-search> ' Search Posts span.block__header__item @@ -19,16 +19,16 @@ h1 Discussion Forums = for forum <- @forums do tr td.table--communication-list__name - => link(forum.name, to: Routes.forum_path(@conn, :show, forum)) + => link(forum.name, to: ~p"/forums/#{forum}") .small-text = forum.description td.table--communication-list__stats.hidden--mobile = forum.topic_count td.table--communication-list__stats.hidden--mobile = forum.post_count td.table--communication-list__last-post = if forum.last_post do strong - => link(forum.last_post.topic.title, to: Routes.forum_topic_path(@conn, :show, forum.last_post.topic.forum, forum.last_post.topic)) + => link(forum.last_post.topic.title, to: ~p"/forums/#{forum.last_post.topic.forum}/topics/#{forum.last_post.topic}") br - => link("Go to post", to: Routes.forum_topic_path(@conn, :show, forum.last_post.topic.forum, forum.last_post.topic, post_id: forum.last_post.id) <> "#post_#{forum.last_post.id}") + => link("Go to post", to: ~p"/forums/#{forum.last_post.topic.forum}/topics/#{forum.last_post.topic}?#{[post_id: forum.last_post.id]}" <> "#post_#{forum.last_post.id}") ' by => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: forum.last_post, conn: @conn br diff --git a/lib/philomena_web/templates/forum/show.html.slime b/lib/philomena_web/templates/forum/show.html.slime index 1182e7021..c7f7b0899 100644 --- a/lib/philomena_web/templates/forum/show.html.slime +++ b/lib/philomena_web/templates/forum/show.html.slime @@ -1,16 +1,16 @@ -- pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @topics, route: fn p -> Routes.forum_path(@conn, :show, @forum, p) end +- pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @topics, route: fn p -> ~p"/forums/#{@forum}?#{p}" end h1 = @forum.name .block .block__header - => link("Forums", to: Routes.forum_path(@conn, :index)) + => link("Forums", to: ~p"/forums") ' » - => link(@forum.name, to: Routes.forum_path(@conn, :show, @forum)) - a href=Routes.forum_topic_path(@conn, :new, @forum) + => link(@forum.name, to: ~p"/forums/#{@forum}") + a href=~p"/forums/#{@forum}/topics/new" i.fa.icon--padded.small.fa-edit> ' New Topic - a href=Routes.post_path(@conn, :index, pq: "forum:#{@forum.short_name}") + a href=~p"/posts?#{[pq: "forum:#{@forum.short_name}"]}" i.fa.icon--padded.small.fa-search> ' Search Posts span.spacing--left @@ -38,7 +38,7 @@ h1 = @forum.name i.fa.fa-lock = if topic.poll do i.fas.fa-poll-h - =< link(topic.title, to: Routes.forum_topic_path(@conn, :show, @forum, topic)) + =< link(topic.title, to: ~p"/forums/#{@forum}/topics/#{topic}") .small-text ' Posted => pretty_time(topic.created_at) @@ -47,7 +47,7 @@ h1 = @forum.name td.table--communication-list__stats.hidden--mobile = topic.post_count td.table--communication-list__last-post = if topic.last_post do - => link("Go to post", to: Routes.forum_topic_path(@conn, :show, topic.forum, topic, post_id: topic.last_post) <> "#post_#{topic.last_post.id}") + => link("Go to post", to: ~p"/forums/#{topic.forum}/topics/#{topic}?#{[post_id: topic.last_post]}" <> "#post_#{topic.last_post.id}") ' by = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: topic.last_post, conn: @conn br diff --git a/lib/philomena_web/templates/forum/subscription/_subscription.html.slime b/lib/philomena_web/templates/forum/subscription/_subscription.html.slime index b9e4876ad..53fd9b617 100644 --- a/lib/philomena_web/templates/forum/subscription/_subscription.html.slime +++ b/lib/philomena_web/templates/forum/subscription/_subscription.html.slime @@ -1,8 +1,8 @@ elixir: - watch_path = Routes.forum_subscription_path(@conn, :create, @forum) + watch_path = ~p"/forums/#{@forum}/subscription" watch_class = if @watching, do: "hidden", else: "" - unwatch_path = Routes.forum_subscription_path(@conn, :delete, @forum) + unwatch_path = ~p"/forums/#{@forum}/subscription" unwatch_class = if @watching, do: "", else: "hidden" = if @conn.assigns.current_user do @@ -17,7 +17,7 @@ elixir: span.hidden--mobile ' Unsubscribe - else - a href=Routes.session_path(@conn, :new) + a href=~p"/sessions/new" i.fa.fa-bell> span.hidden--mobile ' Subscribe diff --git a/lib/philomena_web/templates/gallery/_gallery.html.slime b/lib/philomena_web/templates/gallery/_gallery.html.slime index d7c18a80f..60ae2d561 100644 --- a/lib/philomena_web/templates/gallery/_gallery.html.slime +++ b/lib/philomena_web/templates/gallery/_gallery.html.slime @@ -1,4 +1,4 @@ -- link = Routes.gallery_path(@conn, :show, @gallery) +- link = ~p"/galleries/#{@gallery}" .media-box a.media-box__header.media-box__header--link href=link title=@gallery.title diff --git a/lib/philomena_web/templates/gallery/edit.html.slime b/lib/philomena_web/templates/gallery/edit.html.slime index fe0fc4af3..80687435f 100644 --- a/lib/philomena_web/templates/gallery/edit.html.slime +++ b/lib/philomena_web/templates/gallery/edit.html.slime @@ -1,3 +1,3 @@ h1 Editing Gallery -= render PhilomenaWeb.GalleryView, "_form.html", conn: @conn, changeset: @changeset, action: Routes.gallery_path(@conn, :update, @gallery) \ No newline at end of file += render PhilomenaWeb.GalleryView, "_form.html", conn: @conn, changeset: @changeset, action: ~p"/galleries/#{@gallery}" \ No newline at end of file diff --git a/lib/philomena_web/templates/gallery/index.html.slime b/lib/philomena_web/templates/gallery/index.html.slime index 4c34cddbc..3f3c143a0 100644 --- a/lib/philomena_web/templates/gallery/index.html.slime +++ b/lib/philomena_web/templates/gallery/index.html.slime @@ -1,5 +1,5 @@ elixir: - route = fn p -> Routes.gallery_path(@conn, :index, p) end + route = fn p -> ~p"/galleries?#{p}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @galleries, route: route, params: [gallery: @conn.params["gallery"]] .column-layout @@ -8,7 +8,7 @@ elixir: .block__content h3 Search Galleries - = form_for @conn, Routes.gallery_path(@conn, :index), [as: :gallery, method: "get", class: "hform"], fn f -> + = form_for @conn, ~p"/galleries", [as: :gallery, method: "get", class: "hform"], fn f -> .field = label f, :title, "Title" .field = text_input f, :title, class: "input hform__text", placeholder: "Gallery title (* as wildcard)" diff --git a/lib/philomena_web/templates/gallery/new.html.slime b/lib/philomena_web/templates/gallery/new.html.slime index 7a555f675..3aa4a7f31 100644 --- a/lib/philomena_web/templates/gallery/new.html.slime +++ b/lib/philomena_web/templates/gallery/new.html.slime @@ -1,3 +1,3 @@ h1 Create a Gallery -= render PhilomenaWeb.GalleryView, "_form.html", conn: @conn, changeset: @changeset, action: Routes.gallery_path(@conn, :create) \ No newline at end of file += render PhilomenaWeb.GalleryView, "_form.html", conn: @conn, changeset: @changeset, action: ~p"/galleries" \ No newline at end of file diff --git a/lib/philomena_web/templates/gallery/show.html.slime b/lib/philomena_web/templates/gallery/show.html.slime index e7d9b8520..f9df18a29 100644 --- a/lib/philomena_web/templates/gallery/show.html.slime +++ b/lib/philomena_web/templates/gallery/show.html.slime @@ -1,7 +1,7 @@ elixir: scope = scope(@conn) - image_url = fn image, hit -> Routes.image_path(@conn, :show, image, Keyword.put(scope, :sort, hit["sort"])) end - route = fn p -> Routes.gallery_path(@conn, :show, @gallery, p) end + image_url = fn image, hit -> ~p"/images/#{image}?#{Keyword.put(scope, :sort, hit["sort"])}" end + route = fn p -> ~p"/galleries/#{@gallery}?#{p}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @images, route: route, params: scope info = render PhilomenaWeb.PaginationView, "_pagination_info.html", page: @images @@ -19,12 +19,12 @@ elixir: .flex__right.page__info = render PhilomenaWeb.ImageView, "_random_button.html", conn: @conn, params: scope - a href=Routes.gallery_report_path(@conn, :new, @gallery) + a href=~p"/galleries/#{@gallery}/reports/new" i.fa.fa-exclamation-triangle> span.hidden--mobile Report = if can?(@conn, :edit, @gallery) do - a href=Routes.gallery_path(@conn, :edit, @gallery) + a href=~p"/galleries/#{@gallery}/edit" i.fas.fa-edit> span.hidden--mobile Edit @@ -33,11 +33,11 @@ elixir: i.fa.fa-sort> ' Rearrange - a.rearrange-button.js-save.hidden href="#" data-click-hide=".js-save,#gallery-rearrange-info" data-click-show=".js-rearrange" data-reorder-path=Routes.gallery_order_path(@conn, :update, @gallery) + a.rearrange-button.js-save.hidden href="#" data-click-hide=".js-save,#gallery-rearrange-info" data-click-show=".js-rearrange" data-reorder-path=~p"/galleries/#{@gallery}/order" i.fa.fa-check> ' Save - a href=Routes.gallery_path(@conn, :delete, @gallery) data-method="delete" data-confirm="Are you really, really sure?" + a href=~p"/galleries/#{@gallery}" data-method="delete" data-confirm="Are you really, really sure?" i.fa.fa-trash> span.hidden--mobile Delete @@ -47,7 +47,7 @@ elixir: .block__header.block__header--light.block__header--sub span A gallery by - => link @gallery.creator.name, to: Routes.profile_path(@conn, :show, @gallery.creator) + => link @gallery.creator.name, to: ~p"/profiles/#{@gallery.creator}" ' with => @gallery.image_count = pluralize("image", "images", @gallery.image_count) diff --git a/lib/philomena_web/templates/gallery/subscription/_subscription.html.slime b/lib/philomena_web/templates/gallery/subscription/_subscription.html.slime index a6dec2469..52fbe519a 100644 --- a/lib/philomena_web/templates/gallery/subscription/_subscription.html.slime +++ b/lib/philomena_web/templates/gallery/subscription/_subscription.html.slime @@ -1,8 +1,8 @@ elixir: - watch_path = Routes.gallery_subscription_path(@conn, :create, @gallery) + watch_path = ~p"/galleries/#{@gallery}/subscription" watch_class = if @watching, do: "hidden", else: "" - unwatch_path = Routes.gallery_subscription_path(@conn, :delete, @gallery) + unwatch_path = ~p"/galleries/#{@gallery}/subscription" unwatch_class = if @watching, do: "", else: "hidden" = if @conn.assigns.current_user do @@ -17,7 +17,7 @@ elixir: span.hidden--mobile ' Unsubscribe - else - a href=Routes.session_path(@conn, :new) + a href=~p"/sessions/new" i.fa.fa-bell> span.hidden--mobile ' Subscribe diff --git a/lib/philomena_web/templates/image/_add_to_gallery_dropdown.html.slime b/lib/philomena_web/templates/image/_add_to_gallery_dropdown.html.slime index ce5a57d2f..b5df278f1 100644 --- a/lib/philomena_web/templates/image/_add_to_gallery_dropdown.html.slime +++ b/lib/philomena_web/templates/image/_add_to_gallery_dropdown.html.slime @@ -8,7 +8,7 @@ .block .block__content.add-to-gallery-list .block__list - a.block__list__link.primary href=Routes.gallery_path(@conn, :index, gallery: [include_image: @image.id]) + a.block__list__link.primary href=~p"/galleries?#{[gallery: [include_image: @image.id]]}" i.fa.fa-table> span.hidden--mobile Featured in @@ -18,24 +18,24 @@ = if present do / Options to remove li id="gallery_#{gallery.id}" - a.block__list__link.js-gallery-add.hidden data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-add" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-remove" data-method="post" data-remote="true" href=Routes.gallery_image_path(@conn, :create, gallery, image_id: @image.id) + a.block__list__link.js-gallery-add.hidden data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-add" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-remove" data-method="post" data-remote="true" href=~p"/galleries/#{gallery}/images?#{[image_id: @image.id]}" = gallery.title - a.block__list__link.active.js-gallery-remove data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-remove" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-add" data-method="delete" data-remote="true" href=Routes.gallery_image_path(@conn, :delete, gallery, image_id: @image.id) + a.block__list__link.active.js-gallery-remove data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-remove" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-add" data-method="delete" data-remote="true" href=~p"/galleries/#{gallery}/images?#{[image_id: @image.id]}" = gallery.title - else / Options to add li id="gallery_#{gallery.id}" - a.block__list__link.js-gallery-add data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-add" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-remove" data-method="post" data-remote="true" href=Routes.gallery_image_path(@conn, :create, gallery, image_id: @image.id) + a.block__list__link.js-gallery-add data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-add" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-remove" data-method="post" data-remote="true" href=~p"/galleries/#{gallery}/images?#{[image_id: @image.id]}" = gallery.title - a.block__list__link.active.js-gallery-remove.hidden data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-remove" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-add" data-method="delete" data-remote="true" href=Routes.gallery_image_path(@conn, :delete, gallery, image_id: @image.id) + a.block__list__link.active.js-gallery-remove.hidden data-fetchcomplete-hide="#gallery_#{gallery.id} .js-gallery-remove" data-fetchcomplete-show="#gallery_#{gallery.id} .js-gallery-add" data-method="delete" data-remote="true" href=~p"/galleries/#{gallery}/images?#{[image_id: @image.id]}" = gallery.title .block__list = if @conn.assigns.current_user do - a.block__list__link.primary href=Routes.gallery_path(@conn, :new, with_image: @image.id) + a.block__list__link.primary href=~p"/galleries/new?#{[with_image: @image.id]}" i.fa.fa-plus> span.hidden--mobile Create a gallery - else - a.block__list__link.primary href=Routes.session_path(@conn, :new) + a.block__list__link.primary href=~p"/sessions/new" i.fa.fa-user-plus> span.hidden--mobile Sign in to create a gallery diff --git a/lib/philomena_web/templates/image/_image_box.html.slime b/lib/philomena_web/templates/image/_image_box.html.slime index e456cd39b..a6a44ce90 100644 --- a/lib/philomena_web/templates/image/_image_box.html.slime +++ b/lib/philomena_web/templates/image/_image_box.html.slime @@ -1,5 +1,5 @@ elixir: - link = assigns[:link] || Routes.image_path(@conn, :show, @image) + link = assigns[:link] || ~p"/images/#{@image}" size_class = case @size do :thumb -> diff --git a/lib/philomena_web/templates/image/_image_container.html.slime b/lib/philomena_web/templates/image/_image_container.html.slime index ee16d628d..bae4fea74 100644 --- a/lib/philomena_web/templates/image/_image_container.html.slime +++ b/lib/philomena_web/templates/image/_image_container.html.slime @@ -1,4 +1,4 @@ -- link = assigns[:link] || Routes.image_path(@conn, :show, @image) +- link = assigns[:link] || ~p"/images/#{@image}" = image_container @conn, @image, @size, fn -> = cond do diff --git a/lib/philomena_web/templates/image/_image_meta.html.slime b/lib/philomena_web/templates/image/_image_meta.html.slime index 75af90f98..95e4ad99d 100644 --- a/lib/philomena_web/templates/image/_image_meta.html.slime +++ b/lib/philomena_web/templates/image/_image_meta.html.slime @@ -1,13 +1,13 @@ .block.block__content .metabar.flex.flex--wrap.layout--centered id="image_meta_#{@image.id}" .metabar__navigation.hidden--phone - a.js-prev href=Routes.image_navigate_path(@conn, :index, @image, [rel: "prev"] ++ scope(@conn)) title="Previous Image (j)" + a.js-prev href=~p"/images/#{@image}/navigate?#{[rel: "prev"] ++ scope(@conn)}" title="Previous Image (j)" i.fa.fa-chevron-left - a.js-up href=Routes.image_navigate_path(@conn, :index, @image, [rel: "find"] ++ scope(@conn)) title="Find this image in the global image list (i)" + a.js-up href=~p"/images/#{@image}/navigate?#{[rel: "find"] ++ scope(@conn)}" title="Find this image in the global image list (i)" i.fa.fa-chevron-up - a.js-next href=Routes.image_navigate_path(@conn, :index, @image, [rel: "next"] ++ scope(@conn)) title="Next Image (k)" + a.js-next href=~p"/images/#{@image}/navigate?#{[rel: "next"] ++ scope(@conn)}" title="Next Image (k)" i.fa.fa-chevron-right - a.js-rand href=Routes.image_random_path(@conn, :index, scope(@conn)) title="Random (r)" + a.js-rand href=~p"/images/random?#{scope(@conn)}" title="Random (r)" i.fa.fa-random .metabar__interactions a.interaction--fave href="#" rel="nofollow" data-image-id=@image.id @@ -34,7 +34,7 @@ .metabar__interactions.hidden--mobile = render PhilomenaWeb.Image.SubscriptionView, "_subscription.html", watching: @watching, image: @image, conn: @conn = render PhilomenaWeb.ImageView, "_add_to_gallery_dropdown.html", image: @image, user_galleries: @user_galleries, conn: @conn - a href=Routes.image_related_path(@conn, :index, @image) title="Related Images" + a href=~p"/images/#{@image}/related" title="Related Images" i.fa.fa-sitemap> span.hidden--mobile Related .metabar__mobile-separator @@ -54,7 +54,7 @@ .metabar__interactions.hidden--desktop.hidden--tablet = render PhilomenaWeb.Image.SubscriptionView, "_subscription.html", watching: @watching, image: @image, conn: @conn = render PhilomenaWeb.ImageView, "_add_to_gallery_dropdown.html", image: @image, user_galleries: @user_galleries, conn: @conn - a href=Routes.image_related_path(@conn, :index, @image) title="Related Images" + a href=~p"/images/#{@image}/related" title="Related Images" i.fa.fa-sitemap a href="#{pretty_url(@image, true, false)}" rel="nofollow" title="View (no tags in filename)" i.fa.fa-eye diff --git a/lib/philomena_web/templates/image/_image_mobile_navigation.html.slime b/lib/philomena_web/templates/image/_image_mobile_navigation.html.slime index 5027c26d7..5a776e045 100644 --- a/lib/philomena_web/templates/image/_image_mobile_navigation.html.slime +++ b/lib/philomena_web/templates/image/_image_mobile_navigation.html.slime @@ -1,10 +1,10 @@ .block.block__content.hidden--desktop.hidden--tablet .metabar__navigation - a.js-prev href=Routes.image_navigate_path(@conn, :index, @image, [rel: "prev"] ++ scope(@conn)) title="Previous Image (j)" + a.js-prev href=~p"/images/#{@image}/navigate?#{[rel: "prev"] ++ scope(@conn)}" title="Previous Image (j)" i.fa.fa-chevron-left - a.js-rand href=Routes.image_random_path(@conn, :index, scope(@conn)) title="Random (r)" + a.js-rand href=~p"/images/random?#{scope(@conn)}" title="Random (r)" i.fa.fa-random - a.js-up href=Routes.image_navigate_path(@conn, :index, @image, [rel: "find"] ++ scope(@conn)) title="Find this image in the global image list (i)" + a.js-up href=~p"/images/#{@image}/navigate?#{[rel: "find"] ++ scope(@conn)}" title="Find this image in the global image list (i)" i.fa.fa-chevron-up - a.js-next href=Routes.image_navigate_path(@conn, :index, @image, [rel: "next"] ++ scope(@conn)) title="Next Image (k)" + a.js-next href=~p"/images/#{@image}/navigate?#{[rel: "next"] ++ scope(@conn)}" title="Next Image (k)" i.fa.fa-chevron-right diff --git a/lib/philomena_web/templates/image/_image_target.html.slime b/lib/philomena_web/templates/image/_image_target.html.slime index 3443c74ae..fba84b3a9 100644 --- a/lib/philomena_web/templates/image/_image_target.html.slime +++ b/lib/philomena_web/templates/image/_image_target.html.slime @@ -8,7 +8,7 @@ p = img_tag(static_path(@conn, "/images/tagblocked.svg"), width: 250, height: 250, data: [click_unfilter: @image.id]) span.filter-explanation - =< link("your current filter", to: Routes.filter_path(@conn, :show, @conn.assigns.current_filter), class: "filter-link") + =< link("your current filter", to: ~p"/filters/#{@conn.assigns.current_filter}", class: "filter-link") ' . = if size == :full and not embed_display do @@ -19,7 +19,7 @@ picture - else .image-show.hidden - a href=Routes.image_path(@conn, :show, @image) title=title_text(@image) + a href=~p"/images/#{@image}" title=title_text(@image) span.imgspoiler - thumb_url = thumb_url(@image, can?(@conn, :show, @image), size) diff --git a/lib/philomena_web/templates/image/_options.html.slime b/lib/philomena_web/templates/image/_options.html.slime index c835e0727..80b1143fe 100644 --- a/lib/philomena_web/templates/image/_options.html.slime +++ b/lib/philomena_web/templates/image/_options.html.slime @@ -2,13 +2,13 @@ #image_options_area .block__header.block__header--js-tabbed - a.button.button--primary href="#" data-click-tab="reporting" data-load-tab=Routes.image_reporting_path(@conn, :show, @image) + a.button.button--primary href="#" data-click-tab="reporting" data-load-tab=~p"/images/#{@image}/reporting" i.fa.fa-exclamation-triangle> | Report a.button.button--primary href="#" data-click-tab="sharing" i.fa.fa-share> | Share - a.button.button--primary href="#" data-click-tab="favoriters" data-load-tab=Routes.image_favorite_path(@conn, :index, @image) + a.button.button--primary href="#" data-click-tab="favoriters" data-load-tab=~p"/images/#{@image}/favorites" i.fa.fa-star> span.hidden--phone | List favoriters @@ -60,7 +60,7 @@ | Copy br textarea.input.input--wide.input--separate-top#bbcode_embed_full_tag rows="2" cols="100" readonly="readonly" - = "[img]#{thumb_url(@image, false, :full)}[/img]\n[url=#{Routes.image_url(@conn, :show, @image)}]View on #{site_name()}[/url]#{source_link}" + = "[img]#{thumb_url(@image, false, :full)}[/img]\n[url=#{url(~p"/images/#{@image}")}]View on #{site_name()}[/url]#{source_link}" p strong> Thumbnailed BBcode a href="#" data-click-copy="#bbcode_embed_thumbnail_tag" @@ -68,11 +68,11 @@ | Copy br textarea.input.input--wide.input--separate-top#bbcode_embed_thumbnail_tag rows="2" cols="100" readonly="readonly" - = "[img]#{thumb_url(@image, false, :medium)}[/img]\n[url=#{Routes.image_url(@conn, :show, @image)}]View on #{site_name()}[/url]#{source_link}" + = "[img]#{thumb_url(@image, false, :medium)}[/img]\n[url=#{url(~p"/images/#{@image}")}]View on #{site_name()}[/url]#{source_link}" = if display_mod_tools? do .block__tab.hidden data-tab="replace" - = form_for @changeset, Routes.image_file_path(@conn, :update, @image), [method: "put", multipart: true], fn f -> + = form_for @changeset, ~p"/images/#{@image}/file", [method: "put", multipart: true], fn f -> #js-image-upload-previews p Upload a file from your computer .field @@ -89,7 +89,7 @@ .block__tab.hidden data-tab="administration" .block.block--danger - a.button> href=Routes.image_scratchpad_path(@conn, :edit, @image) + a.button> href=~p"/images/#{@image}/scratchpad/edit" i.far.fa-edit = if present?(@image.scratchpad) do strong> Mod notes: @@ -98,13 +98,13 @@ em No mod notes present = if not @image.hidden_from_users do - = form_for @changeset, Routes.image_delete_path(@conn, :create, @image), [method: "post"], fn f -> + = form_for @changeset, ~p"/images/#{@image}/delete", [method: "post"], fn f -> = label f, :deletion_reason, "Deletion reason (cannot be empty)" .field.field--inline = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Rule violation", required: true = submit "Delete", class: "button button--danger button--separate-left" - else - = form_for @changeset, Routes.image_delete_path(@conn, :update, @image), [method: "put"], fn f -> + = form_for @changeset, ~p"/images/#{@image}/delete", [method: "put"], fn f -> = label f, :deletion_reason, "Deletion reason (cannot be empty)" .field.field--inline = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Rule violation", required: true @@ -112,18 +112,18 @@ .flex.flex--spaced-out.flex--wrap = if not @image.hidden_from_users do - = form_for @changeset, Routes.image_feature_path(@conn, :create, @image), [method: "post"], fn _f -> + = form_for @changeset, ~p"/images/#{@image}/feature", [method: "post"], fn _f -> .field p Marks the image as featured = submit "Feature", data: [confirm: "Are you really, really sure?"], class: "button button--success" - else - = button_to "Restore", Routes.image_delete_path(@conn, :delete, @image), method: "delete", class: "button button--success" + = button_to "Restore", ~p"/images/#{@image}/delete", method: "delete", class: "button button--success" - = form_for @changeset, Routes.image_repair_path(@conn, :create, @image), [method: "post"], fn _f -> + = form_for @changeset, ~p"/images/#{@image}/repair", [method: "post"], fn _f -> .field = submit "Repair", class: "button button--success" - = form_for @changeset, Routes.image_hash_path(@conn, :delete, @image), [method: "delete"], fn _f -> + = form_for @changeset, ~p"/images/#{@image}/hash", [method: "delete"], fn _f -> .field p Allows reuploading the image .flex.flex--end-bunched @@ -132,24 +132,24 @@ br .flex.flex--spaced-out = if @image.commenting_allowed do - = button_to "Lock commenting", Routes.image_comment_lock_path(@conn, :create, @image), method: "post", class: "button" + = button_to "Lock commenting", ~p"/images/#{@image}/comment_lock", method: "post", class: "button" - else - = button_to "Unlock commenting", Routes.image_comment_lock_path(@conn, :delete, @image), method: "delete", class: "button" + = button_to "Unlock commenting", ~p"/images/#{@image}/comment_lock", method: "delete", class: "button" = if @image.description_editing_allowed do - = button_to "Lock description editing", Routes.image_description_lock_path(@conn, :create, @image), method: "post", class: "button" + = button_to "Lock description editing", ~p"/images/#{@image}/description_lock", method: "post", class: "button" - else - = button_to "Unlock description editing", Routes.image_description_lock_path(@conn, :delete, @image), method: "delete", class: "button" + = button_to "Unlock description editing", ~p"/images/#{@image}/description_lock", method: "delete", class: "button" = if @image.tag_editing_allowed do - = button_to "Lock tag editing", Routes.image_tag_lock_path(@conn, :create, @image), method: "post", class: "button" + = button_to "Lock tag editing", ~p"/images/#{@image}/tag_lock", method: "post", class: "button" - else - = button_to "Unlock tag editing", Routes.image_tag_lock_path(@conn, :delete, @image), method: "delete", class: "button" + = button_to "Unlock tag editing", ~p"/images/#{@image}/tag_lock", method: "delete", class: "button" br .flex.flex--spaced-out - = link "Lock specific tags", to: Routes.image_tag_lock_path(@conn, :show, @image), class: "button" + = link "Lock specific tags", to: ~p"/images/#{@image}/tag_lock", class: "button" = if not @image.approved and can?(@conn, :approve, @image) do - = button_to "Approve image", Routes.image_approve_path(@conn, :create, @image), method: "post", class: "button button--success", data: [confirm: "Are you sure?"] + = button_to "Approve image", ~p"/images/#{@image}/approve", method: "post", class: "button button--success", data: [confirm: "Are you sure?"] = if @image.hidden_from_users and can?(@conn, :destroy, @image) do - = button_to "Destroy image", Routes.image_destroy_path(@conn, :create, @image), method: "post", class: "button button--danger", data: [confirm: "This action is IRREVERSIBLE. Are you sure?"] + = button_to "Destroy image", ~p"/images/#{@image}/destroy", method: "post", class: "button button--danger", data: [confirm: "This action is IRREVERSIBLE. Are you sure?"] diff --git a/lib/philomena_web/templates/image/_random_button.html.slime b/lib/philomena_web/templates/image/_random_button.html.slime index 296bd711e..a004dbeae 100644 --- a/lib/philomena_web/templates/image/_random_button.html.slime +++ b/lib/philomena_web/templates/image/_random_button.html.slime @@ -1,3 +1,3 @@ -a href=Routes.image_random_path(@conn, :index, @params) title="Random Image" +a href=~p"/images/random?#{@params}" title="Random Image" i.fa.fa-random> span.hidden--mobile Random Image diff --git a/lib/philomena_web/templates/image/_source.html.slime b/lib/philomena_web/templates/image/_source.html.slime index c7b9ea078..6eecc792d 100644 --- a/lib/philomena_web/templates/image/_source.html.slime +++ b/lib/philomena_web/templates/image/_source.html.slime @@ -1,6 +1,6 @@ .js-sourcesauce - has_sources = Enum.any?(@image.sources) - = form_for @changeset, Routes.image_source_path(@conn, :update, @image), [method: "put", class: "hidden", id: "source-form", data: [remote: "true"]], fn f -> + = form_for @changeset, ~p"/images/#{@image}/sources", [method: "put", class: "hidden", id: "source-form", data: [remote: "true"]], fn f -> = if can?(@conn, :edit_metadata, @image) and !@conn.assigns.current_ban do = if @changeset.action do .alert.alert-danger @@ -54,14 +54,14 @@ - else ' Add = if @source_change_count > 0 do - a.button href=Routes.image_source_change_path(@conn, :index, @image) title="Source history" + a.button href=~p"/images/#{@image}/source_changes" title="Source history" i.fa.fa-history> spanspan.hide-mobile> History | ( = @source_change_count | ) = if can?(@conn, :hide, @image) and not hide_staff_tools?(@conn) do - = form_for @changeset, Routes.image_source_history_path(@conn, :delete, @image), [method: "delete"], fn _f -> + = form_for @changeset, ~p"/images/#{@image}/source_history", [method: "delete"], fn _f -> a.button.button--warning type="submit" data-confirm="Are you really, really sure?" title="Wipe sources" i.fas.fa-eraser> ' Wipe diff --git a/lib/philomena_web/templates/image/_tags.html.slime b/lib/philomena_web/templates/image/_tags.html.slime index fa5f8e0ce..52ae9c422 100644 --- a/lib/philomena_web/templates/image/_tags.html.slime +++ b/lib/philomena_web/templates/image/_tags.html.slime @@ -12,7 +12,7 @@ ' The following tags have been restricted on this image: code= Enum.map_join(@image.locked_tags, ", ", & &1.name) - = form_for @changeset, Routes.image_tag_path(@conn, :update, @image), [id: "tags-form", method: "put", data: [remote: "true"]], fn f -> + = form_for @changeset, ~p"/images/#{@image}/tags", [id: "tags-form", method: "put", data: [remote: "true"]], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -69,7 +69,7 @@ i.fas.fa-edit> ' Edit = if @tag_change_count > 0 do - a.button href=Routes.image_tag_change_path(@conn, :index, @image) title="Tag history" + a.button href=~p"/images/#{@image}/tag_changes" title="Tag history" i.fa.fa-history> | History ( = @tag_change_count diff --git a/lib/philomena_web/templates/image/_uploader.html.slime b/lib/philomena_web/templates/image/_uploader.html.slime index ca40bd72c..6062657e3 100644 --- a/lib/philomena_web/templates/image/_uploader.html.slime +++ b/lib/philomena_web/templates/image/_uploader.html.slime @@ -11,7 +11,7 @@ span.image-uploader i.fas.fa-eye = if can?(@conn, :show, :ip_address) do - = form_for @changeset, Routes.image_uploader_path(@conn, :update, @image), [class: "block__content hidden", id: "uploader-form", data: [remote: "true", method: "put"]], fn f -> + = form_for @changeset, ~p"/images/#{@image}/uploader", [class: "block__content hidden", id: "uploader-form", data: [remote: "true", method: "put"]], fn f -> => label f, :username, "Uploader" => text_input f, :username, value: username(@image.user), class: "input input--short input--small" @@ -21,6 +21,6 @@ span.image-uploader .image-anonymous.hidden = if @image.anonymous do - = button_to "Reveal author", Routes.image_anonymous_path(@conn, :delete, @image), class: "button button--small", method: "delete", data: [confirm: "Are you really, really sure?"] + = button_to "Reveal author", ~p"/images/#{@image}/anonymous", class: "button button--small", method: "delete", data: [confirm: "Are you really, really sure?"] - else - = button_to "Hide author", Routes.image_anonymous_path(@conn, :create, @image), class: "button button--small", method: "create", data: [confirm: "Are you really, really sure?"] + = button_to "Hide author", ~p"/images/#{@image}/anonymous", class: "button button--small", method: "create", data: [confirm: "Are you really, really sure?"] diff --git a/lib/philomena_web/templates/image/comment/_form.html.slime b/lib/philomena_web/templates/image/comment/_form.html.slime index d1745a11a..84bf2bd6d 100644 --- a/lib/philomena_web/templates/image/comment/_form.html.slime +++ b/lib/philomena_web/templates/image/comment/_form.html.slime @@ -1,6 +1,6 @@ - options = if(assigns[:remote], do: [data: [remote: "true"], id: "js-comment-form"], else: []) -= form_for @changeset, Routes.image_comment_path(@conn, :create, @image), options, fn f -> += form_for @changeset, ~p"/images/#{@image}/comments", options, fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/image/comment/edit.html.slime b/lib/philomena_web/templates/image/comment/edit.html.slime index d7183a608..1b17c96a4 100644 --- a/lib/philomena_web/templates/image/comment/edit.html.slime +++ b/lib/philomena_web/templates/image/comment/edit.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.image_comment_path(@conn, :update, @comment.image, @comment), fn f -> += form_for @changeset, ~p"/images/#{@comment.image}/comments/#{@comment}", fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/image/comment/history/index.html.slime b/lib/philomena_web/templates/image/comment/history/index.html.slime index b28ee0661..71fc21861 100644 --- a/lib/philomena_web/templates/image/comment/history/index.html.slime +++ b/lib/philomena_web/templates/image/comment/history/index.html.slime @@ -2,7 +2,7 @@ h1 ' Viewing last 25 versions of comment by = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, conn: @conn ' on image - a href=Routes.image_path(@conn, :show, @comment.image) + a href=~p"/images/#{@comment.image}" | # = @comment.image_id diff --git a/lib/philomena_web/templates/image/comment/index.html.slime b/lib/philomena_web/templates/image/comment/index.html.slime index e42c0b357..b351ff132 100644 --- a/lib/philomena_web/templates/image/comment/index.html.slime +++ b/lib/philomena_web/templates/image/comment/index.html.slime @@ -1,5 +1,5 @@ elixir: - route = fn p -> Routes.image_comment_path(@conn, :index, @image, p) end + route = fn p -> ~p"/images/#{@image}/comments?#{p}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @comments, route: route .block diff --git a/lib/philomena_web/templates/image/deleted.html.slime b/lib/philomena_web/templates/image/deleted.html.slime index 4177b0a36..fc425b36d 100644 --- a/lib/philomena_web/templates/image/deleted.html.slime +++ b/lib/philomena_web/templates/image/deleted.html.slime @@ -4,7 +4,7 @@ h1 This image has been merged into another image p ' This image was merged into image - => link "##{@image.duplicate_id}", to: Routes.image_path(@conn, :show, @image.duplicate_id) + => link "##{@image.duplicate_id}", to: ~p"/images/#{@image.duplicate_id}" ' because it was determined to be a duplicate of that image. - else diff --git a/lib/philomena_web/templates/image/description/_form.html.slime b/lib/philomena_web/templates/image/description/_form.html.slime index 6b4c4543a..e47b5d3ff 100644 --- a/lib/philomena_web/templates/image/description/_form.html.slime +++ b/lib/philomena_web/templates/image/description/_form.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.image_description_path(@conn, :update, @image), [class: "block hidden", id: "description-form", data: [remote: "true"]], fn f -> += form_for @changeset, ~p"/images/#{@image}/description", [class: "block hidden", id: "description-form", data: [remote: "true"]], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/image/favorite/index.html.slime b/lib/philomena_web/templates/image/favorite/index.html.slime index 1e606aada..ed239797a 100644 --- a/lib/philomena_web/templates/image/favorite/index.html.slime +++ b/lib/philomena_web/templates/image/favorite/index.html.slime @@ -4,7 +4,7 @@ h5 = pluralize("user", "users", @image.faves_count) = for fave <- Enum.sort_by(@image.faves, & String.downcase(&1.user.name)) do - => link fave.user.name, to: Routes.profile_path(@conn, :show, fave.user), class: "interaction-user-list-item" + => link fave.user.name, to: ~p"/profiles/#{fave.user}", class: "interaction-user-list-item" = if @has_votes do h5 @@ -14,8 +14,8 @@ h5 = for upvote <- Enum.sort_by(@image.upvotes, & String.downcase(&1.user.name)) do span.interaction-user-list-item - => link upvote.user.name, to: Routes.profile_path(@conn, :show, upvote.user) - => link "(x)", to: Routes.image_tamper_path(@conn, :create, @image, user_id: upvote.user_id), method: "post" + => link upvote.user.name, to: ~p"/profiles/#{upvote.user}" + => link "(x)", to: ~p"/images/#{@image}/tamper?#{[user_id: upvote.user_id]}", method: "post" h5 ' Downvoted by @@ -24,8 +24,8 @@ h5 = for downvote <- Enum.sort_by(@image.downvotes, & String.downcase(&1.user.name)) do span.interaction-user-list-item - => link downvote.user.name, to: Routes.profile_path(@conn, :show, downvote.user) - => link "(x)", to: Routes.image_tamper_path(@conn, :create, @image, user_id: downvote.user_id), method: "post" + => link downvote.user.name, to: ~p"/profiles/#{downvote.user}" + => link "(x)", to: ~p"/images/#{@image}/tamper?#{[user_id: downvote.user_id]}", method: "post" h5 ' Hidden by @@ -33,4 +33,4 @@ h5 = pluralize("user", "users", @image.hides_count) = for hide <- Enum.sort_by(@image.hides, & String.downcase(&1.user.name)) do - => link hide.user.name, to: Routes.profile_path(@conn, :show, hide.user), class: "interaction-user-list-item" + => link hide.user.name, to: ~p"/profiles/#{hide.user}", class: "interaction-user-list-item" diff --git a/lib/philomena_web/templates/image/index.html.slime b/lib/philomena_web/templates/image/index.html.slime index 389fe61be..ff4e27f62 100644 --- a/lib/philomena_web/templates/image/index.html.slime +++ b/lib/philomena_web/templates/image/index.html.slime @@ -3,9 +3,9 @@ elixir: params = assigns[:params] || assigns[:scope] || [] scope = assigns[:scope] || [] tags = assigns[:tags] || [] - route = assigns[:route] || fn p -> Routes.image_path(@conn, :index, p) end - image_url = fn image -> Routes.image_path(@conn, :show, image, scope) end - sorted_url = fn image, hit -> Routes.image_path(@conn, :show, image, Keyword.put(scope, :sort, hit["sort"])) end + route = assigns[:route] || fn p -> ~p"/images?#{p}" end + image_url = fn image -> ~p"/images/#{image}?#{scope}" end + sorted_url = fn image, hit -> ~p"/images/#{image}?#{Keyword.put(scope, :sort, hit["sort"])}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @images, route: route, params: params info = render PhilomenaWeb.PaginationView, "_pagination_info.html", page: @images diff --git a/lib/philomena_web/templates/image/new.html.slime b/lib/philomena_web/templates/image/new.html.slime index f290077f9..68fe74716 100644 --- a/lib/philomena_web/templates/image/new.html.slime +++ b/lib/philomena_web/templates/image/new.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.image_path(@conn, :create), [multipart: true], fn f -> += form_for @changeset, ~p"/images", [multipart: true], fn f -> .dnp-warning h4 ' Read the diff --git a/lib/philomena_web/templates/image/related/index.html.slime b/lib/philomena_web/templates/image/related/index.html.slime index fe7bd41c6..366469693 100644 --- a/lib/philomena_web/templates/image/related/index.html.slime +++ b/lib/philomena_web/templates/image/related/index.html.slime @@ -8,4 +8,4 @@ .media-list = for image <- @images do - = render PhilomenaWeb.ImageView, "_image_box.html", image: image, link: Routes.image_path(@conn, :show, image), size: :thumb, conn: @conn + = render PhilomenaWeb.ImageView, "_image_box.html", image: image, link: ~p"/images/#{image}", size: :thumb, conn: @conn diff --git a/lib/philomena_web/templates/image/reporting/show.html.slime b/lib/philomena_web/templates/image/reporting/show.html.slime index 274b07ebd..6dbf96509 100644 --- a/lib/philomena_web/templates/image/reporting/show.html.slime +++ b/lib/philomena_web/templates/image/reporting/show.html.slime @@ -1,4 +1,4 @@ -a href=Routes.image_report_path(@conn, :new, @image) +a href=~p"/images/#{@image}/reports/new" button.button i.fa.fa-exclamation-triangle> ' General reporting @@ -15,7 +15,7 @@ a href=Routes.image_report_path(@conn, :new, @image) - else p ' You must - a> href=Routes.session_path(@conn, :new) log in + a> href=~p"/sessions/new" log in ' to report duplicate images. - target_reports = Enum.filter(@dupe_reports, & &1.duplicate_of_image_id == @image.id) diff --git a/lib/philomena_web/templates/image/scratchpad/edit.html.slime b/lib/philomena_web/templates/image/scratchpad/edit.html.slime index 31e37f9f9..bd27ab6e0 100644 --- a/lib/philomena_web/templates/image/scratchpad/edit.html.slime +++ b/lib/philomena_web/templates/image/scratchpad/edit.html.slime @@ -1,8 +1,8 @@ h1 ' Editing moderation notes for image - = link "##{@image.id}", to: Routes.image_path(@conn, :show, @image) + = link "##{@image.id}", to: ~p"/images/#{@image}" -= form_for @changeset, Routes.image_scratchpad_path(@conn, :update, @image), fn f -> += form_for @changeset, ~p"/images/#{@image}/scratchpad", fn f -> .field = textarea f, :scratchpad, placeholder: "Scratchpad contents", class: "input input--wide" diff --git a/lib/philomena_web/templates/image/show.html.slime b/lib/philomena_web/templates/image/show.html.slime index 7b9c08421..d40ee7b97 100644 --- a/lib/philomena_web/templates/image/show.html.slime +++ b/lib/philomena_web/templates/image/show.html.slime @@ -27,5 +27,5 @@ - true -> - #comments data-current-url=Routes.image_comment_path(@conn, :index, @image) data-loaded="true" + #comments data-current-url=~p"/images/#{@image}/comments" data-loaded="true" = render PhilomenaWeb.Image.CommentView, "index.html", image: @image, comments: @comments, conn: @conn diff --git a/lib/philomena_web/templates/image/source_change/index.html.slime b/lib/philomena_web/templates/image/source_change/index.html.slime index 712f434a1..bd5f4a574 100644 --- a/lib/philomena_web/templates/image/source_change/index.html.slime +++ b/lib/philomena_web/templates/image/source_change/index.html.slime @@ -1,10 +1,10 @@ h1 ' Source changes for - a href=Routes.image_path(@conn, :show, @image) + a href=~p"/images/#{@image}" | image # = @image.id -- route = fn p -> Routes.image_source_change_path(@conn, :index, @image, p) end +- route = fn p -> ~p"/images/#{@image}/source_changes?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @source_changes, route: route, conn: @conn = render PhilomenaWeb.SourceChangeView, "index.html", conn: @conn, source_changes: @source_changes, pagination: pagination \ No newline at end of file diff --git a/lib/philomena_web/templates/image/subscription/_subscription.html.slime b/lib/philomena_web/templates/image/subscription/_subscription.html.slime index 8df7e54c4..aac749923 100644 --- a/lib/philomena_web/templates/image/subscription/_subscription.html.slime +++ b/lib/philomena_web/templates/image/subscription/_subscription.html.slime @@ -1,8 +1,8 @@ elixir: - watch_path = Routes.image_subscription_path(@conn, :create, @image) + watch_path = ~p"/images/#{@image}/subscription" watch_class = if @watching, do: "hidden", else: "" - unwatch_path = Routes.image_subscription_path(@conn, :delete, @image) + unwatch_path = ~p"/images/#{@image}/subscription" unwatch_class = if @watching, do: "", else: "hidden" = if @conn.assigns.current_user do @@ -15,6 +15,6 @@ elixir: i.fa.fa-bell-slash span.hidden--phone< Unsubscribe - else - a href=Routes.session_path(@conn, :new) + a href=~p"/sessions/new" i.fa.fa-bell span.hidden--phone< Subscribe diff --git a/lib/philomena_web/templates/image/tag_change/index.html.slime b/lib/philomena_web/templates/image/tag_change/index.html.slime index f246f12e6..1ddd57f94 100644 --- a/lib/philomena_web/templates/image/tag_change/index.html.slime +++ b/lib/philomena_web/templates/image/tag_change/index.html.slime @@ -1,10 +1,10 @@ h1 ' Tag changes for - a href=Routes.image_path(@conn, :show, @image) + a href=~p"/images/#{@image}" | image # = @image.id -- route = fn p -> Routes.image_tag_change_path(@conn, :index, @image, p) end +- route = fn p -> ~p"/images/#{@image}/tag_changes?#{p}" end - params = if @conn.params["added"], do: [added: @conn.params["added"]] - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @tag_changes, route: route, conn: @conn, params: params @@ -13,8 +13,8 @@ h1 span | Display only: - = link "Removed", to: Routes.image_tag_change_path(@conn, :index, @image, added: 0) - = link "Added", to: Routes.image_tag_change_path(@conn, :index, @image, added: 1) - = link "All", to: Routes.image_tag_change_path(@conn, :index, @image) + = link "Removed", to: ~p"/images/#{@image}/tag_changes?#{[added: 0]}" + = link "Added", to: ~p"/images/#{@image}/tag_changes?#{[added: 1]}" + = link "All", to: ~p"/images/#{@image}/tag_changes" = render PhilomenaWeb.TagChangeView, "index.html", conn: @conn, tag_changes: @tag_changes, pagination: pagination diff --git a/lib/philomena_web/templates/image/tag_lock/show.html.slime b/lib/philomena_web/templates/image/tag_lock/show.html.slime index 81a52cdfc..e5a9841d6 100644 --- a/lib/philomena_web/templates/image/tag_lock/show.html.slime +++ b/lib/philomena_web/templates/image/tag_lock/show.html.slime @@ -4,7 +4,7 @@ h1 | Editing locked tags on image # = @image.id -= form_for @changeset, Routes.image_tag_lock_path(@conn, :update, @image), fn f -> += form_for @changeset, ~p"/images/#{@image}/tag_lock", fn f -> .field = render PhilomenaWeb.TagView, "_tag_editor.html", f: f, name: :tag_input, type: :edit, extra: [value: tag_input] = error_tag f, :tag_input diff --git a/lib/philomena_web/templates/ip_profile/show.html.slime b/lib/philomena_web/templates/ip_profile/show.html.slime index c752d9ffc..eb3ac25c5 100644 --- a/lib/philomena_web/templates/ip_profile/show.html.slime +++ b/lib/philomena_web/templates/ip_profile/show.html.slime @@ -3,24 +3,24 @@ h1 ' 's IP profile ul - li = link "View images this IP has uploaded", to: Routes.search_path(@conn, :index, q: "ip:#{@ip}") - li = link "View comments this IP has posted", to: Routes.comment_path(@conn, :index, cq: "ip:#{@ip}") - li = link "View posts this IP has made", to: Routes.post_path(@conn, :index, pq: "ip:#{@ip}") + li = link "View images this IP has uploaded", to: ~p"/search?#{[q: "ip:#{@ip}"]}" + li = link "View comments this IP has posted", to: ~p"/comments?#{[cq: "ip:#{@ip}"]}" + li = link "View posts this IP has made", to: ~p"/posts?#{[pq: "ip:#{@ip}"]}" = render PhilomenaWeb.BanView, "_bans.html", bans: @subnet_bans, conn: @conn h2 Administration Options ul - li = link "View tag changes", to: Routes.ip_profile_tag_change_path(@conn, :index, to_string(@ip)) - li = link "View source URL history", to: Routes.ip_profile_source_change_path(@conn, :index, to_string(@ip)) - li = link "View reports this IP has made", to: Routes.admin_report_path(@conn, :index, rq: "ip:#{@ip}") - li = link "View IP ban history", to: Routes.admin_subnet_ban_path(@conn, :index, ip: to_string(@ip)) - li = link "Ban this sucker", to: Routes.admin_subnet_ban_path(@conn, :new, specification: to_string(@ip)) + li = link "View tag changes", to: ~p"/ip_profiles/#{to_string(@ip)}/tag_changes" + li = link "View source URL history", to: ~p"/ip_profiles/#{to_string(@ip)}/source_changes" + li = link "View reports this IP has made", to: ~p"/admin/reports?#{[rq: "ip:#{@ip}"]}" + li = link "View IP ban history", to: ~p"/admin/subnet_bans?#{[ip: to_string(@ip)]}" + li = link "Ban this sucker", to: ~p"/admin/subnet_bans/new?#{[specification: to_string(@ip)]}" h2 Actions ul - li = link "Revert all tag changes", to: Routes.tag_change_full_revert_path(@conn, :create, [ip: to_string(@ip)]), data: [confirm: "Are you really, really sure?", method: "create"] - li = link "...the button above didn't work (use carefully, this is resource-intensive)", to: Routes.tag_change_full_revert_path(@conn, :create, [ip: to_string(@ip), batch_size: 1]), data: [confirm: "Please confirm that you're aware that this may crash the site and are ready to take on the full wrath of the admins if it does so after you press this button.", method: "create"] + li = link "Revert all tag changes", to: ~p"/tag_changes/full_revert?#{[ip: to_string(@ip)]}", data: [confirm: "Are you really, really sure?", method: "create"] + li = link "...the button above didn't work (use carefully, this is resource-intensive)", to: ~p"/tag_changes/full_revert?#{[ip: to_string(@ip), batch_size: 1]}", data: [confirm: "Please confirm that you're aware that this may crash the site and are ready to take on the full wrath of the admins if it does so after you press this button.", method: "create"] h4 Observed users table.table @@ -34,7 +34,7 @@ table.table = for uip <- @user_ips do tr td - = link uip.user.name, to: Routes.profile_path(@conn, :show, uip.user) + = link uip.user.name, to: ~p"/profiles/#{uip.user}" td => uip.uses ' times diff --git a/lib/philomena_web/templates/ip_profile/source_change/index.html.slime b/lib/philomena_web/templates/ip_profile/source_change/index.html.slime index 5bf1bea10..d31a79680 100644 --- a/lib/philomena_web/templates/ip_profile/source_change/index.html.slime +++ b/lib/philomena_web/templates/ip_profile/source_change/index.html.slime @@ -2,7 +2,7 @@ h1 ' Source changes by = @ip -- route = fn p -> Routes.ip_profile_source_change_path(@conn, :index, to_string(@ip), p) end +- route = fn p -> ~p"/ip_profiles/#{to_string(@ip)}/source_changes?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @source_changes, route: route, conn: @conn = render PhilomenaWeb.SourceChangeView, "index.html", conn: @conn, source_changes: @source_changes, pagination: pagination diff --git a/lib/philomena_web/templates/ip_profile/tag_change/index.html.slime b/lib/philomena_web/templates/ip_profile/tag_change/index.html.slime index 53a3c5e85..55741974a 100644 --- a/lib/philomena_web/templates/ip_profile/tag_change/index.html.slime +++ b/lib/philomena_web/templates/ip_profile/tag_change/index.html.slime @@ -2,7 +2,7 @@ h1 ' Tag changes by = @ip -- route = fn p -> Routes.ip_profile_tag_change_path(@conn, :index, to_string(@ip), p) end +- route = fn p -> ~p"/ip_profiles/#{to_string(@ip)}/tag_changes?#{p}" end - params = if @conn.params["added"], do: [added: @conn.params["added"]] - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @tag_changes, route: route, conn: @conn, params: params @@ -11,8 +11,8 @@ h1 span | Display only: - = link "Removed", to: Routes.ip_profile_tag_change_path(@conn, :index, to_string(@ip), added: 0) - = link "Added", to: Routes.ip_profile_tag_change_path(@conn, :index, to_string(@ip), added: 1) - = link "All", to: Routes.ip_profile_tag_change_path(@conn, :index, to_string(@ip)) + = link "Removed", to: ~p"/ip_profiles/#{to_string(@ip)}/tag_changes?#{[added: 0]}" + = link "Added", to: ~p"/ip_profiles/#{to_string(@ip)}/tag_changes?#{[added: 1]}" + = link "All", to: ~p"/ip_profiles/#{to_string(@ip)}/tag_changes" = render PhilomenaWeb.TagChangeView, "index.html", conn: @conn, tag_changes: @tag_changes, pagination: pagination diff --git a/lib/philomena_web/templates/layout/_header.html.slime b/lib/philomena_web/templates/layout/_header.html.slime index b88d7295b..5d6b20e2e 100644 --- a/lib/philomena_web/templates/layout/_header.html.slime +++ b/lib/philomena_web/templates/layout/_header.html.slime @@ -7,7 +7,7 @@ header span.hidden--mobile = site_name() a.header__link.hidden--mobile href="/images/new" title="Upload" i.fa.fa-upload - = form_for @conn, Routes.search_path(@conn, :index), [method: "get", class: "header__search", enforce_utf8: false], fn f -> + = form_for @conn, ~p"/search", [method: "get", class: "header__search", enforce_utf8: false], fn f -> input.input.input--text.header__input.header__input--text#q name="q" title="For terms all required, separate with ',' or 'AND'; also supports 'OR' for optional terms and '-' or 'NOT' for negation. Search with a blank query for more options or click the ? for syntax help." value=@conn.params["q"] placeholder="Search" autocapitalize="none" = if present?(@conn.params["sf"]) do @@ -44,19 +44,19 @@ header span.hidden--tablet< Filters .header__search - = form_for @user_changeset, Routes.filter_current_path(@conn, :update), [class: "header__filter-form", id: "filter-quick-form"], fn f -> + = form_for @user_changeset, ~p"/filters/current", [class: "header__filter-form", id: "filter-quick-form"], fn f -> = select f, :current_filter_id, @available_filters, name: "id", id: "filter-quick-menu", class: "input header__input", data: [change_submit: "#filter-quick-form"], autocomplete: "off" .header__search - = form_for @user_changeset, Routes.filter_spoiler_type_path(@conn, :update), [class: "header__filter-form hidden--mobile", id: "spoiler-quick-form"], fn f -> + = form_for @user_changeset, ~p"/filters/spoiler_type", [class: "header__filter-form hidden--mobile", id: "spoiler-quick-form"], fn f -> = select f, :spoiler_type, @spoiler_types, id: "spoiler-quick-menu", class: "input header__input", data: [change_submit: "#spoiler-quick-form"], autocomplete: "off" .dropdown - a.header__link.header__link--user href=Routes.profile_path(@conn, :show, @current_user) + a.header__link.header__link--user href=~p"/profiles/#{@current_user}" = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @current_user}, class: "avatar--navbar" i.fa.fa-chevron-down nav.dropdown__content.dropdown__content--right.hidden--mobile.js-burger-links - a.dropdown__item href=Routes.profile_path(@conn, :show, @current_user) + a.dropdown__item href=~p"/profiles/#{@current_user}" .dropdown__icon i.fa.icon--fixed.fa-user .dropdown__text @@ -74,7 +74,7 @@ header .dropdown__icon i.fa.icon--fixed.fa-arrow-up .dropdown__text Upvotes - a.dropdown__item href=Routes.gallery_path(@conn, :index, gallery: [creator: @current_user.name]) + a.dropdown__item href=~p"/galleries?#{[gallery: [creator: @current_user.name]]}" .dropdown__icon i.fa.icon--fixed.fa-image .dropdown__text Galleries @@ -90,7 +90,7 @@ header .dropdown__icon i.fa.icon--fixed.fa-pen-square .dropdown__text Posts - a.dropdown__item href=Routes.profile_artist_link_path(@conn, :index, @current_user) + a.dropdown__item href=~p"/profiles/#{@current_user}/artist_links" .dropdown__icon i.fa.icon--fixed.fa-link .dropdown__text Links @@ -103,12 +103,12 @@ header .dropdown__icon i.fa.icon--fixed.fa-cogs .dropdown__text Settings - a.dropdown__item href=Routes.registration_path(@conn, :edit) + a.dropdown__item href=~p"/registrations/edit" .dropdown__icon i.fa.icon--fixed.fa-user .dropdown__text Account hr.dropdown__separator - a.dropdown__item href=Routes.session_path(@conn, :delete) data-method="delete" + a.dropdown__item href=~p"/sessions" data-method="delete" .dropdown__icon i.fa.icon--fixed.fa-sign-out-alt .dropdown__text Logout @@ -121,9 +121,9 @@ header a.header__link href="/settings/edit" i.fa.icon--fixed.fa-cogs.hidden--desktop> | Settings - a.header__link href=Routes.registration_path(@conn, :new) + a.header__link href=~p"/registrations/new" | Register - a.header__link href=Routes.session_path(@conn, :new) + a.header__link href=~p"/sessions/new" | Login nav.header__secondary = render PhilomenaWeb.LayoutView, "_header_navigation.html", assigns diff --git a/lib/philomena_web/templates/layout/_header_navigation.html.slime b/lib/philomena_web/templates/layout/_header_navigation.html.slime index 7ceb5b968..dff5426a4 100644 --- a/lib/philomena_web/templates/layout/_header_navigation.html.slime +++ b/lib/philomena_web/templates/layout/_header_navigation.html.slime @@ -30,7 +30,7 @@ i.fa.fa-caret-down< nav.dropdown__content = for forum <- @conn.assigns.forums do - a.dropdown__link href=Routes.forum_path(@conn, :show, forum) + a.dropdown__link href=~p"/forums/#{forum}" = forum.name hr.dropdown__separator diff --git a/lib/philomena_web/templates/layout/_header_staff_links.html.slime b/lib/philomena_web/templates/layout/_header_staff_links.html.slime index 0e376ba70..8543fa076 100644 --- a/lib/philomena_web/templates/layout/_header_staff_links.html.slime +++ b/lib/philomena_web/templates/layout/_header_staff_links.html.slime @@ -7,87 +7,87 @@ .dropdown__content.js-burger-links = if manages_site_notices?(@conn) do - = link to: Routes.admin_site_notice_path(@conn, :index) do + = link to: ~p"/admin/site_notices" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-info-circle .dropdown__text Site Notices = if manages_users?(@conn) do - = link to: Routes.admin_user_path(@conn, :index) do + = link to: ~p"/admin/users" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-users .dropdown__text Users = if manages_forums?(@conn) do - = link to: Routes.admin_forum_path(@conn, :index) do + = link to: ~p"/admin/forums" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-paragraph .dropdown__text Forums = if manages_ads?(@conn) do - = link to: Routes.admin_advert_path(@conn, :index) do + = link to: ~p"/admin/adverts" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-shopping-bag .dropdown__text Adverts = if manages_badges?(@conn) do - = link to: Routes.admin_badge_path(@conn, :index) do + = link to: ~p"/admin/badges" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-trophy .dropdown__text Badges = if manages_static_pages?(@conn) do - = link to: Routes.page_path(@conn, :index) do + = link to: ~p"/pages" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-sticky-note .dropdown__text Pages = if manages_mod_notes?(@conn) do - = link to: Routes.admin_mod_note_path(@conn, :index) do + = link to: ~p"/admin/mod_notes" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-sticky-note .dropdown__text Mod Notes = if can_see_moderation_log?(@conn) do - = link to: Routes.moderation_log_path(@conn, :index) do + = link to: ~p"/moderation_logs" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-list-alt .dropdown__text Mod Logs = if @pending_approval_count do - = link to: Routes.admin_approval_path(@conn, :index), class: "header__link", title: "Approval Queue" do + = link to: ~p"/admin/approvals", class: "header__link", title: "Approval Queue" do ' Q span.header__counter__admin = @pending_approval_count = if @duplicate_report_count do - = link to: Routes.duplicate_report_path(@conn, :index), class: "header__link", title: "Duplicates" do + = link to: ~p"/duplicate_reports", class: "header__link", title: "Duplicates" do ' D span.header__counter__admin = @duplicate_report_count = if @report_count do - = link to: Routes.admin_report_path(@conn, :index), class: "header__link", title: "Reports" do + = link to: ~p"/admin/reports", class: "header__link", title: "Reports" do ' R span.header__counter__admin = @report_count = if @artist_link_count do - = link to: Routes.admin_artist_link_path(@conn, :index), class: "header__link", title: "Artist Links" do + = link to: ~p"/admin/artist_links", class: "header__link", title: "Artist Links" do ' L span.header__counter__admin = @artist_link_count = if @dnp_entry_count do - = link to: Routes.admin_dnp_entry_path(@conn, :index), class: "header__link", title: "DNP Requests" do + = link to: ~p"/admin/dnp_entries", class: "header__link", title: "DNP Requests" do ' S span.header__counter__admin = @dnp_entry_count @@ -99,19 +99,19 @@ i.fa.fa-caret-down .dropdown__content.dropdown__content--right.js-burger-links - = link to: Routes.admin_user_ban_path(@conn, :index) do + = link to: ~p"/admin/user_bans" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-user .dropdown__text User Bans - = link to: Routes.admin_subnet_ban_path(@conn, :index) do + = link to: ~p"/admin/subnet_bans" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-network-wired .dropdown__text IP Bans - = link to: Routes.admin_fingerprint_ban_path(@conn, :index) do + = link to: ~p"/admin/fingerprint_bans" do .dropdown__item .dropdown__icon i.fa.icon--padded.small.fa-desktop diff --git a/lib/philomena_web/templates/layout/_opengraph.html.slime b/lib/philomena_web/templates/layout/_opengraph.html.slime index f7fac7631..81daa32dc 100644 --- a/lib/philomena_web/templates/layout/_opengraph.html.slime +++ b/lib/philomena_web/templates/layout/_opengraph.html.slime @@ -12,7 +12,7 @@ meta name="format-detection" content="telephone=no" meta name="keywords" content=tag_list(image) meta name="description" content=description meta property="og:title" content=description - meta property="og:url" content=Routes.image_url(@conn, :show, image) + meta property="og:url" content=url(~p"/images/#{image}") = for tag <- artist_tags(image.tags) do meta property="dc:creator" content=tag.name_in_namespace @@ -20,9 +20,9 @@ meta name="format-detection" content="telephone=no" = if image_has_sources(image) do meta property="foaf:primaryTopic" content=image_first_source(image) - link rel="alternate" type="application/json+oembed" href=Routes.api_json_oembed_url(@conn, :index, url: Routes.image_path(@conn, :show, image)) title="oEmbed JSON Profile" + link rel="alternate" type="application/json+oembed" href=url(~p"/api/v1/json/oembed?#{[url: ~p"/images/#{image}"]}") title="oEmbed JSON Profile" - link rel="canonical" href=Routes.image_url(@conn, :show, image) + link rel="canonical" href=url(~p"/images/#{image}") = cond do - image.image_mime_type == "video/webm" and not filtered -> diff --git a/lib/philomena_web/templates/layout/app.html.slime b/lib/philomena_web/templates/layout/app.html.slime index 2ddd1e5b7..0578f2882 100644 --- a/lib/philomena_web/templates/layout/app.html.slime +++ b/lib/philomena_web/templates/layout/app.html.slime @@ -11,7 +11,7 @@ html lang="en" =< site_name() - else =<> site_name() - link rel="stylesheet" href=Routes.static_path(@conn, "/css/application.css") + link rel="stylesheet" href=~p"/css/application.css" link rel="stylesheet" href=stylesheet_path(@conn, @current_user) = if is_nil(@current_user) do link rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)" diff --git a/lib/philomena_web/templates/message/_message.html.slime b/lib/philomena_web/templates/message/_message.html.slime index 8522e124e..09864ccfb 100644 --- a/lib/philomena_web/templates/message/_message.html.slime +++ b/lib/philomena_web/templates/message/_message.html.slime @@ -9,7 +9,7 @@ article.block.communication p ul.horizontal-list li - = link(to: Routes.conversation_message_approve_path(@conn, :create, @message.conversation_id, @message), data: [confirm: "Are you sure?"], method: "post", class: "button") do + = link(to: ~p"/conversations/#{@message.conversation_id}/messages/#{@message}/approve", data: [confirm: "Are you sure?"], method: "post", class: "button") do i.fas.fa-check> ' Approve diff --git a/lib/philomena_web/templates/moderation_log/index.html.slime b/lib/philomena_web/templates/moderation_log/index.html.slime index a19273051..e2c81eefc 100644 --- a/lib/philomena_web/templates/moderation_log/index.html.slime +++ b/lib/philomena_web/templates/moderation_log/index.html.slime @@ -1,5 +1,5 @@ elixir: - route = fn p -> Routes.moderation_log_path(@conn, :index, p) end + route = fn p -> ~p"/moderation_logs?#{p}" end pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @moderation_logs, route: route, conn: @conn h1 Listing Moderation Logs diff --git a/lib/philomena_web/templates/notification/_channel.html.slime b/lib/philomena_web/templates/notification/_channel.html.slime index 3e40d6f33..1fc57157c 100644 --- a/lib/philomena_web/templates/notification/_channel.html.slime +++ b/lib/philomena_web/templates/notification/_channel.html.slime @@ -1,14 +1,14 @@ .flex.flex--centered.flex__grow div strong> - = link @notification.actor.title, to: Routes.channel_path(@conn, :show, @notification.actor) + = link @notification.actor.title, to: ~p"/channels/#{@notification.actor}" =<> @notification.action => pretty_time @notification.updated_at .flex.flex--centered.flex--no-wrap - a.button.button--separate-right title="Delete" href=Routes.channel_read_path(@conn, :create, @notification.actor) data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button.button--separate-right title="Delete" href=~p"/channels/#{@notification.actor}/read" data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-trash - a.button title="Unsubscribe" href=Routes.channel_subscription_path(@conn, :delete, @notification.actor) data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button title="Unsubscribe" href=~p"/channels/#{@notification.actor}/subscription" data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-bell-slash \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_forum.html.slime b/lib/philomena_web/templates/notification/_forum.html.slime index 512c49eae..f7edb1985 100644 --- a/lib/philomena_web/templates/notification/_forum.html.slime +++ b/lib/philomena_web/templates/notification/_forum.html.slime @@ -9,17 +9,17 @@ ' titled strong> - = link topic.title, to: Routes.forum_topic_path(@conn, :show, forum, topic) + = link topic.title, to: ~p"/forums/#{forum}/topics/#{topic}" ' in - => link forum.name, to: Routes.forum_path(@conn, :show, forum) + => link forum.name, to: ~p"/forums/#{forum}" => pretty_time @notification.updated_at .flex.flex--centered.flex--no-wrap - a.button.button--separate-right title="Delete" href=Routes.forum_read_path(@conn, :create, forum) data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button.button--separate-right title="Delete" href=~p"/forums/#{forum}/read" data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-trash - a.button title="Unsubscribe" href=Routes.forum_subscription_path(@conn, :delete, forum) data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button title="Unsubscribe" href=~p"/forums/#{forum}/subscription" data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-bell-slash \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_gallery.html.slime b/lib/philomena_web/templates/notification/_gallery.html.slime index 767cbc737..09e3eccc7 100644 --- a/lib/philomena_web/templates/notification/_gallery.html.slime +++ b/lib/philomena_web/templates/notification/_gallery.html.slime @@ -4,13 +4,13 @@ => @notification.action strong> - = link @notification.actor.title, to: Routes.gallery_path(@conn, :show, @notification.actor) + = link @notification.actor.title, to: ~p"/galleries/#{@notification.actor}" => pretty_time @notification.updated_at .flex.flex--centered.flex--no-wrap - a.button.button--separate-right title="Delete" href=Routes.gallery_read_path(@conn, :create, @notification.actor) data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button.button--separate-right title="Delete" href=~p"/galleries/#{@notification.actor}/read" data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-trash - a.button title="Unsubscribe" href=Routes.gallery_subscription_path(@conn, :delete, @notification.actor) data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button title="Unsubscribe" href=~p"/galleries/#{@notification.actor}/subscription" data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-bell-slash \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_image.html.slime b/lib/philomena_web/templates/notification/_image.html.slime index ebedb4229..89814c399 100644 --- a/lib/philomena_web/templates/notification/_image.html.slime +++ b/lib/philomena_web/templates/notification/_image.html.slime @@ -7,13 +7,13 @@ => @notification.action strong> - = link "##{@notification.actor_id}", to: Routes.image_path(@conn, :show, @notification.actor) <> "#comments" + = link "##{@notification.actor_id}", to: ~p"/images/#{@notification.actor}" <> "#comments" => pretty_time @notification.updated_at .flex.flex--centered.flex--no-wrap - a.button.button--separate-right title="Delete" href=Routes.image_read_path(@conn, :create, @notification.actor) data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button.button--separate-right title="Delete" href=~p"/images/#{@notification.actor}/read" data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-trash - a.button title="Unsubscribe" href=Routes.image_subscription_path(@conn, :delete, @notification.actor) data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button title="Unsubscribe" href=~p"/images/#{@notification.actor}/subscription" data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-bell-slash \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/_topic.html.slime b/lib/philomena_web/templates/notification/_topic.html.slime index 1a1c5ff64..5ecefcfd8 100644 --- a/lib/philomena_web/templates/notification/_topic.html.slime +++ b/lib/philomena_web/templates/notification/_topic.html.slime @@ -7,13 +7,13 @@ => @notification.action strong> - = link topic.title, to: Routes.forum_topic_path(@conn, :show, topic.forum, topic, post_id: post.id) <> "#post_#{post.id}" + = link topic.title, to: ~p"/forums/#{topic.forum}/topics/#{topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" => pretty_time @notification.updated_at .flex.flex--centered.flex--no-wrap - a.button.button--separate-right title="Delete" href=Routes.forum_topic_read_path(@conn, :create, topic.forum, topic) data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button.button--separate-right title="Delete" href=~p"/forums/#{topic.forum}/topics/#{topic}/read" data-method="post" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-trash - a.button title="Unsubscribe" href=Routes.forum_topic_subscription_path(@conn, :delete, topic.forum, topic) data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" + a.button title="Unsubscribe" href=~p"/forums/#{topic.forum}/topics/#{topic}/subscription" data-method="delete" data-remote="true" data-fetchcomplete-hide="#notification-#{@notification.id}" i.fa.fa-bell-slash \ No newline at end of file diff --git a/lib/philomena_web/templates/notification/index.html.slime b/lib/philomena_web/templates/notification/index.html.slime index be72130c2..4503680dd 100644 --- a/lib/philomena_web/templates/notification/index.html.slime +++ b/lib/philomena_web/templates/notification/index.html.slime @@ -1,4 +1,4 @@ -- route = fn p -> Routes.notification_path(@conn, :index, p) end +- route = fn p -> ~p"/notifications?#{p}" end h1 Notification Area .walloftext diff --git a/lib/philomena_web/templates/page/edit.html.slime b/lib/philomena_web/templates/page/edit.html.slime index 2ac4387b8..f141d06b5 100644 --- a/lib/philomena_web/templates/page/edit.html.slime +++ b/lib/philomena_web/templates/page/edit.html.slime @@ -1,3 +1,3 @@ h1 Editing static page -= render PhilomenaWeb.PageView, "_form.html", changeset: @changeset, action: Routes.page_path(@conn, :update, @static_page), conn: @conn += render PhilomenaWeb.PageView, "_form.html", changeset: @changeset, action: ~p"/pages/#{@static_page}", conn: @conn diff --git a/lib/philomena_web/templates/page/history/index.html.slime b/lib/philomena_web/templates/page/history/index.html.slime index cc7429f7b..805492eb0 100644 --- a/lib/philomena_web/templates/page/history/index.html.slime +++ b/lib/philomena_web/templates/page/history/index.html.slime @@ -1,6 +1,6 @@ h1 ' Revision history for - = link @static_page.title, to: Routes.page_path(@conn, :show, @static_page) + = link @static_page.title, to: ~p"/pages/#{@static_page}" table.table thead @@ -12,7 +12,7 @@ table.table tbody = for version <- @versions do tr - td = link version.user.name, to: Routes.profile_path(@conn, :show, version.user) + td = link version.user.name, to: ~p"/profiles/#{version.user}" td = pretty_time(version.created_at) td.static-page__diff = for diff <- version.difference do diff --git a/lib/philomena_web/templates/page/index.html.slime b/lib/philomena_web/templates/page/index.html.slime index 88731d64e..5e535faa2 100644 --- a/lib/philomena_web/templates/page/index.html.slime +++ b/lib/philomena_web/templates/page/index.html.slime @@ -8,7 +8,7 @@ table.table tbody = for static_page <- @static_pages do tr - td = link static_page.title, to: Routes.page_path(@conn, :show, static_page) + td = link static_page.title, to: ~p"/pages/#{static_page}" br -= link "New static page", to: Routes.page_path(@conn, :new) += link "New static page", to: ~p"/pages/new" diff --git a/lib/philomena_web/templates/page/new.html.slime b/lib/philomena_web/templates/page/new.html.slime index 48ad0fd1f..c2b7b2086 100644 --- a/lib/philomena_web/templates/page/new.html.slime +++ b/lib/philomena_web/templates/page/new.html.slime @@ -1,3 +1,3 @@ h1 New static page -= render PhilomenaWeb.PageView, "_form.html", changeset: @changeset, action: Routes.page_path(@conn, :create), conn: @conn += render PhilomenaWeb.PageView, "_form.html", changeset: @changeset, action: ~p"/pages", conn: @conn diff --git a/lib/philomena_web/templates/page/show.html.slime b/lib/philomena_web/templates/page/show.html.slime index 9eadee5f0..f5bc82cce 100644 --- a/lib/philomena_web/templates/page/show.html.slime +++ b/lib/philomena_web/templates/page/show.html.slime @@ -4,11 +4,11 @@ p => pretty_time(@static_page.updated_at) p - => link to: Routes.page_history_path(@conn, :index, @static_page) do + => link to: ~p"/pages/#{@static_page}/history" do i.fa.fa-history> ' Revision history = if can?(@conn, :edit, Philomena.StaticPages.StaticPage) do - =< link to: Routes.page_path(@conn, :edit, @static_page) do + =< link to: ~p"/pages/#{@static_page}/edit" do i.fa.fa-edit> ' Edit diff --git a/lib/philomena_web/templates/password/edit.html.slime b/lib/philomena_web/templates/password/edit.html.slime index 0046b4d90..00cf1f717 100644 --- a/lib/philomena_web/templates/password/edit.html.slime +++ b/lib/philomena_web/templates/password/edit.html.slime @@ -1,6 +1,6 @@ h1 Reset password -= form_for @changeset, Routes.password_path(@conn, :update, @token), fn f -> += form_for @changeset, ~p"/passwords/#{@token}", fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/password/new.html.slime b/lib/philomena_web/templates/password/new.html.slime index 20b84e858..3aac61b5c 100644 --- a/lib/philomena_web/templates/password/new.html.slime +++ b/lib/philomena_web/templates/password/new.html.slime @@ -3,7 +3,7 @@ p ' Provide the email address you signed up with and we will email you ' password reset instructions. -= form_for :user, Routes.password_path(@conn, :create), fn f -> += form_for :user, ~p"/passwords", fn f -> .field = email_input f, :email, class: "input", placeholder: "Email", required: true diff --git a/lib/philomena_web/templates/post/_post.html.slime b/lib/philomena_web/templates/post/_post.html.slime index 42a0b046d..bdf7fd23d 100644 --- a/lib/philomena_web/templates/post/_post.html.slime +++ b/lib/philomena_web/templates/post/_post.html.slime @@ -1,10 +1,10 @@ -- post_link = Routes.forum_topic_path(@conn, :show, @post.topic.forum, @post.topic, post_id: @post.id) <> "#post_#{@post.id}" +- post_link = ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}" - options = if true do .flex__spacer.hidden--desktop a.communication__interaction.hidden--desktop title="Link to post" href=post_link i.fa.fa-link = if not is_nil(@post.edited_at) and can?(@conn, :show, @post) do - a href=Routes.forum_topic_post_history_path(@conn, :index, @post.topic.forum, @post.topic, @post) + a href=~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/history" ' Edited => pretty_time(@post.edited_at) @@ -23,7 +23,7 @@ article.block.communication id="post_#{@post.id}" p ul.horizontal-list li - = link(to: Routes.forum_topic_post_approve_path(@conn, :create, @post.topic.forum, @post.topic, @post), data: [confirm: "Are you sure?"], method: "post", class: "button") do + = link(to: ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/approve", data: [confirm: "Are you sure?"], method: "post", class: "button") do i.fas.fa-check> ' Approve li @@ -31,7 +31,7 @@ article.block.communication id="post_#{@post.id}" i.fa.fa-times> ' Reject - = form_for :post, Routes.forum_topic_post_hide_path(@conn, :create, @post.topic.forum, @post.topic, @post), [class: "togglable-delete-form hidden flex", id: "inline-reject-form-post-#{@post.id}"], fn f -> + = form_for :post, ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/hide", [class: "togglable-delete-form hidden flex", id: "inline-reject-form-post-#{@post.id}"], fn f -> = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-reject-reason-post-#{@post.id}", required: true = submit "Delete", class: "button" @@ -45,11 +45,11 @@ article.block.communication id="post_#{@post.id}" = if can?(@conn, :hide, @post) and not hide_staff_tools?(@conn) do = cond do - @post.hidden_from_users and not @post.destroyed_content -> - = link(to: Routes.forum_topic_post_hide_path(@conn, :delete, @post.topic.forum, @post.topic, @post), data: [confirm: "Are you sure?"], method: "delete", class: "communication__interaction") do + = link(to: ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/hide", data: [confirm: "Are you sure?"], method: "delete", class: "communication__interaction") do i.fas.fa-check> ' Restore = if can?(@conn, :delete, @post) do - = link(to: Routes.forum_topic_post_delete_path(@conn, :create, @post.topic.forum, @post.topic, @post), data: [confirm: "Are you sure?"], method: "post", class: "communication__interaction") do + = link(to: ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/delete", data: [confirm: "Are you sure?"], method: "post", class: "communication__interaction") do i.fas.fa-times> ' Delete Contents @@ -66,6 +66,6 @@ article.block.communication id="post_#{@post.id}" .communication__info =<> link_to_fingerprint(@conn, @post.fingerprint) - = form_for :post, Routes.forum_topic_post_hide_path(@conn, :create, @post.topic.forum, @post.topic, @post), [class: "togglable-delete-form hidden flex", id: "inline-del-form-post-#{@post.id}"], fn f -> + = form_for :post, ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/hide", [class: "togglable-delete-form hidden flex", id: "inline-del-form-post-#{@post.id}"], fn f -> = text_input f, :deletion_reason, class: "input input--wide", placeholder: "Deletion Reason", id: "inline-del-reason-post-#{@post.id}", required: true = submit "Delete", class: "button" diff --git a/lib/philomena_web/templates/post/_post_options.html.slime b/lib/philomena_web/templates/post/_post_options.html.slime index bd9aaf437..bb1353957 100644 --- a/lib/philomena_web/templates/post/_post_options.html.slime +++ b/lib/philomena_web/templates/post/_post_options.html.slime @@ -1,9 +1,9 @@ div - - link_path = Routes.forum_topic_path(@conn, :show, @post.topic.forum, @post.topic, post_id: @post.id) <> "#post_#{@post.id}" + - link_path = ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}" - safe_author = markdown_safe_author(@post) - quote_body = if @post.hidden_from_users, do: "", else: @post.body - a.communication__interaction href=Routes.forum_topic_post_report_path(@conn, :new, @post.topic.forum, @post.topic, @post) + a.communication__interaction href=~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/reports/new" i.fa.fa-flag> ' Report @@ -18,6 +18,6 @@ div = if can?(@conn, :edit, @post) do span.owner-options strong - a.communication__interaction href=Routes.forum_topic_post_path(@conn, :edit, @post.topic.forum, @post.topic, @post) + a.communication__interaction href=~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/edit" i.fas.fa-edit> ' Edit diff --git a/lib/philomena_web/templates/post/index.html.slime b/lib/philomena_web/templates/post/index.html.slime index f0d9dcd43..c4f00f9cf 100644 --- a/lib/philomena_web/templates/post/index.html.slime +++ b/lib/philomena_web/templates/post/index.html.slime @@ -1,6 +1,6 @@ h1 Posts -= form_for :posts, Routes.post_path(@conn, :index), [method: "get", class: "hform", enforce_utf8: false], fn f -> += form_for :posts, ~p"/posts", [method: "get", class: "hform", enforce_utf8: false], fn f -> .field = text_input f, :pq, name: :pq, value: @conn.params["pq"], class: "input hform__text", placeholder: "Search posts", autocapitalize: "none" = submit "Search", class: "hform__button button" @@ -14,17 +14,17 @@ h2 Search Results = cond do - Enum.any?(@posts) -> - - route = fn p -> Routes.post_path(@conn, :index, p) end + - route = fn p -> ~p"/posts?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @posts, route: route, params: [pq: @conn.params["pq"]], conn: @conn = for {body, post} <- @posts, post.topic.hidden_from_users == false do div h3 - =<> link post.topic.forum.name, to: Routes.forum_path(@conn, :show, post.topic.forum) + =<> link post.topic.forum.name, to: ~p"/forums/#{post.topic.forum}" | » - =<> link post.topic.title, to: Routes.forum_topic_path(@conn, :show, post.topic.forum, post.topic) + =<> link post.topic.title, to: ~p"/forums/#{post.topic.forum}/topics/#{post.topic}" | » - - post_link = Routes.forum_topic_path(@conn, :show, post.topic.forum, post.topic, post_id: post.id) <> "#post_#{post.id}" + - post_link = ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post.id]}" <> "#post_#{post.id}" = if post.topic_position == 0 do =<> link "Topic Opener", to: post_link - else @@ -79,28 +79,28 @@ table.table td Literal td Matches the author of this post. Anonymous authors will never match this term. td - code = link "author:Joey", to: Routes.post_path(@conn, :index, pq: "author:Joey") + code = link "author:Joey", to: ~p"/posts?#{[pq: "author:Joey"]}" tr td code body td Full Text td Matches the body of this post. This is the default field. td - code = link "body:test", to: Routes.post_path(@conn, :index, pq: "body:test") + code = link "body:test", to: ~p"/posts?#{[pq: "body:test"]}" tr td code created_at td Date/Time Range td Matches the creation time of this post. td - code = link "created_at:2015", to: Routes.post_path(@conn, :index, pq: "created_at:2015") + code = link "created_at:2015", to: ~p"/posts?#{[pq: "created_at:2015"]}" tr td code id td Numeric Range td Matches the numeric surrogate key for this post. td - code = link "id:1000000", to: Routes.post_path(@conn, :index, pq: "id:1000000") + code = link "id:1000000", to: ~p"/posts?#{[pq: "id:1000000"]}" tr td code my @@ -109,46 +109,46 @@ table.table code> my:posts ' matches posts you have posted if you are signed in. td - code = link "my:posts", to: Routes.post_path(@conn, :index, pq: "my:posts") + code = link "my:posts", to: ~p"/posts?#{[pq: "my:posts"]}" tr td code subject td Full Text td Matches the title of the topic. td - code = link "subject:time wasting thread", to: Routes.post_path(@conn, :index, pq: "subject:time wasting thread") + code = link "subject:time wasting thread", to: ~p"/posts?#{[pq: "subject:time wasting thread"]}" tr td code topic_id td Literal td Matches the numeric surrogate key for the topic this post belongs to. td - code = link "topic_id:7000", to: Routes.post_path(@conn, :index, pq: "topic_id:7000") + code = link "topic_id:7000", to: ~p"/posts?#{[pq: "topic_id:7000"]}" tr td code topic_position td Numeric Range td Matches the offset from the beginning of the topic of this post. Positions begin at 0. td - code = link "topic_position:0", to: Routes.post_path(@conn, :index, pq: "topic_position:0") + code = link "topic_position:0", to: ~p"/posts?#{[pq: "topic_position:0"]}" tr td code updated_at td Date/Time Range td Matches the creation or last edit time of this post. td - code = link "updated_at.gte:2 weeks ago", to: Routes.post_path(@conn, :index, pq: "updated_at.gte:2 weeks ago") + code = link "updated_at.gte:2 weeks ago", to: ~p"/posts?#{[pq: "updated_at.gte:2 weeks ago"]}" tr td code user_id td Literal td Matches posts with the specified user_id. Anonymous users will never match this term. td - code = link "user_id:211190", to: Routes.post_path(@conn, :index, pq: "user_id:211190") + code = link "user_id:211190", to: ~p"/posts?#{[pq: "user_id:211190"]}" tr td code forum td Literal td Matches the short name for the forum this post belongs to. td - code = link "forum:meta", to: Routes.post_path(@conn, :index, pq: "forum:meta") + code = link "forum:meta", to: ~p"/posts?#{[pq: "forum:meta"]}" diff --git a/lib/philomena_web/templates/profile/_about_me.html.slime b/lib/philomena_web/templates/profile/_about_me.html.slime index 55e5d732a..345e2b2c2 100644 --- a/lib/philomena_web/templates/profile/_about_me.html.slime +++ b/lib/philomena_web/templates/profile/_about_me.html.slime @@ -8,6 +8,6 @@ = if can?(@conn, :edit_description, @user) do = if @user.description not in [nil, ""] do hr.separator.separator--secondary - a.button href=Routes.profile_description_path(@conn, :edit, @user) + a.button href=~p"/profiles/#{@user}/description/edit" i.fa.fa-edit> | Edit diff --git a/lib/philomena_web/templates/profile/_admin_block.html.slime b/lib/philomena_web/templates/profile/_admin_block.html.slime index 565fa33bb..88eb5b82d 100644 --- a/lib/philomena_web/templates/profile/_admin_block.html.slime +++ b/lib/philomena_web/templates/profile/_admin_block.html.slime @@ -11,7 +11,7 @@ i.fa.icon--padded.small.fa-filter> ' Current Filter: = if @filter do - = link @filter.name, to: Routes.filter_path(@conn, :show, @filter) + = link @filter.name, to: ~p"/filters/#{@filter}" - else em ' (none) @@ -56,121 +56,121 @@ .profile-top__options.js-admin__options__toggle.hidden ul.profile-admin__options__column li - = link to: Routes.profile_detail_path(@conn, :index, @user) do + = link to: ~p"/profiles/#{@user}/details" do i.fa.icon--padded.small.fa-eye span.admin__button View Details li - = link to: Routes.search_path(@conn, :index, q: "upvoted_by_id:#{@user.id}") do + = link to: ~p"/search?#{[q: "upvoted_by_id:#{@user.id}"]}" do i.fa.icon--padded.small.fa-arrow-up span.admin__button Upvotes li - = link to: Routes.search_path(@conn, :index, q: "downvoted_by_id:#{@user.id}") do + = link to: ~p"/search?#{[q: "downvoted_by_id:#{@user.id}"]}" do i.fa.icon--padded.small.fa-arrow-down span.admin__button Downvotes li - = link to: Routes.search_path(@conn, :index, q: "hidden_by_id:#{@user.id}") do + = link to: ~p"/search?#{[q: "hidden_by_id:#{@user.id}"]}" do i.fa.icon--padded.small.fa-eye-slash span.admin__button Hidden Images li - = link to: Routes.admin_report_path(@conn, :index, rq: "user_id:#{@user.id}") do + = link to: ~p"/admin/reports?#{[rq: "user_id:#{@user.id}"]}" do i.fa.icon--padded.small.fa-exclamation span.admin__button Reports li - = link to: Routes.profile_ip_history_path(@conn, :index, @user) do + = link to: ~p"/profiles/#{@user}/ip_history" do i.fab.icon--padded.small.fa-internet-explorer span.admin__button IP History li - = link to: Routes.profile_fp_history_path(@conn, :index, @user) do + = link to: ~p"/profiles/#{@user}/fp_history" do i.fa.icon--padded.small.fa-desktop span.admin__button FP History li - = link to: Routes.profile_alias_path(@conn, :index, @user) do + = link to: ~p"/profiles/#{@user}/aliases" do i.fa.icon--padded.small.fa-users span.admin__button Potential Aliases = if can?(@conn, :index, %Philomena.Donations.Donation{}) do li - = link to: Routes.admin_donation_user_path(@conn, :show, @user) do + = link to: ~p"/admin/donations/user/#{@user}" do i.fas.icon--padded.small.fa-dollar-sign span.admin__button Donations ul.profile-admin__options__column = if can?(@conn, :edit, @user) do li - = link to: Routes.admin_user_path(@conn, :edit, @user) do + = link to: ~p"/admin/users/#{@user}/edit" do i.fas.icon--padded.small.fa-edit span.admin__button Edit User li - = link to: Routes.admin_user_force_filter_path(@conn, :new, @user) do + = link to: ~p"/admin/users/#{@user}/force_filter/new" do i.fas.faw-fw.fa-filter span.admin__button Force Filter = if @forced do li - = link to: Routes.profile_detail_path(@conn, :index, @user) do + = link to: ~p"/profiles/#{@user}/details" do i.fa.icon--padded.small.fa-eye span.admin__button View Details li - = link to: Routes.search_path(@conn, :index, q: "upvoted_by_id:#{@user.id}") do + = link to: ~p"/search?#{[q: "upvoted_by_id:#{@user.id}"]}" do i.fa.icon--padded.small.fa-arrow-up span.admin__button Upvotes li - = link to: Routes.search_path(@conn, :index, q: "downvoted_by_id:#{@user.id}") do + = link to: ~p"/search?#{[q: "downvoted_by_id:#{@user.id}"]}" do i.fa.icon--padded.small.fa-arrow-down span.admin__button Downvotes li - = link to: Routes.admin_user_unlock_path(@conn, :create, @user), data: [method: "post"] do + = link to: ~p"/admin/users/#{@user}/unlock", data: [method: "post"] do i.fas.icon--padded.small.fa-unlock span.admin__button Unlock Account li - = link to: Routes.admin_user_wipe_path(@conn, :create, @user), data: [confirm: "This is irreversible, destroying all identifying information including email. Are you sure?", method: "post"] do + = link to: ~p"/admin/users/#{@user}/wipe", data: [confirm: "This is irreversible, destroying all identifying information including email. Are you sure?", method: "post"] do i.fas.icon--padded.small.fa-eraser span.admin__button Wipe PII = if can?(@conn, :edit, %Philomena.ArtistLinks.ArtistLink{}) do li - = link to: Routes.profile_artist_link_path(@conn, :new, @user) do + = link to: ~p"/profiles/#{@user}/artist_links/new" do i.fa.icon--padded.small.fa-link span.admin__button Add Artist Link li - = link to: Routes.admin_user_force_filter_path(@conn, :new, @user) do + = link to: ~p"/admin/users/#{@user}/force_filter/new" do i.fas.faw-fw.fa-filter span.admin__button Force Filter ul.profile-admin__options__column = if can?(@conn, :index, Philomena.Users.User) do li - = link to: Routes.admin_user_api_key_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do + = link to: ~p"/admin/users/#{@user}/api_key", data: [confirm: "Are you really, really sure?", method: "delete"] do i.fas.icon--padded.small.fa-key span.admin__button Reset API key li = if @user.verified do - = link to: Routes.admin_user_verification_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do + = link to: ~p"/admin/users/#{@user}/verification", data: [confirm: "Are you really, really sure?", method: "delete"] do i.fas.icon--padded.small.fa-user-times span.admin__button Revoke Verification - else - = link to: Routes.admin_user_verification_path(@conn, :create, @user), data: [confirm: "Are you really, really sure?", method: "create"] do + = link to: ~p"/admin/users/#{@user}/verification", data: [confirm: "Are you really, really sure?", method: "create"] do i.fas.icon--padded.small.fa-user-check span.admin__button Grant Verification ul.profile-admin__options__column = if can?(@conn, :index, Philomena.Users.User) do li - = link to: Routes.admin_user_vote_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do + = link to: ~p"/admin/users/#{@user}/votes", data: [confirm: "Are you really, really sure?", method: "delete"] do i.far.icon--padded.small.fa-file-excel span.admin__button Remove All Votes/Faves li - = link to: Routes.admin_user_downvote_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do + = link to: ~p"/admin/users/#{@user}/downvotes", data: [confirm: "Are you really, really sure?", method: "delete"] do i.fa.icon--padded.small.fa-arrow-down span.admin__button Remove All Downvotes = if @user.role == "user" and can?(@conn, :revert, Philomena.TagChanges.TagChange) do li - = link to: Routes.tag_change_full_revert_path(@conn, :create, [user_id: @user.id]), data: [confirm: "Are you really, really sure?", method: "create"] do + = link to: ~p"/tag_changes/full_revert?#{[user_id: @user.id]}", data: [confirm: "Are you really, really sure?", method: "create"] do i.fa.icon--padded.small.fa-tag span.admin__button Revert All Tag Changes diff --git a/lib/philomena_web/templates/profile/_commission.html.slime b/lib/philomena_web/templates/profile/_commission.html.slime index db8e8d46f..be102740d 100644 --- a/lib/philomena_web/templates/profile/_commission.html.slime +++ b/lib/philomena_web/templates/profile/_commission.html.slime @@ -21,18 +21,18 @@ br br - = link "More information", to: Routes.profile_commission_path(@conn, :show, @user) + = link "More information", to: ~p"/profiles/#{@user}/commission" - current?(@user, @conn.assigns.current_user) -> = if Enum.any?(@conn.assigns.user.verified_links) do em ' You don't have any commission information listed yet. - => link "Click here", to: Routes.profile_commission_path(@conn, :new, @user) + => link "Click here", to: ~p"/profiles/#{@user}/commission/new" ' to set it up. - else em ' You must have a verified Artist Link to create a commission page. - => link "Click here", to: Routes.profile_artist_link_path(@conn, :new, @user) + => link "Click here", to: ~p"/profiles/#{@user}/artist_links/new" ' to set one up. - true -> diff --git a/lib/philomena_web/templates/profile/_recent_comments.html.slime b/lib/philomena_web/templates/profile/_recent_comments.html.slime index 9f18fe0e7..d1567483a 100644 --- a/lib/philomena_web/templates/profile/_recent_comments.html.slime +++ b/lib/philomena_web/templates/profile/_recent_comments.html.slime @@ -2,7 +2,7 @@ .block .block__header span Recent Comments - = link "View all", to: Routes.comment_path(@conn, :index, cq: "user_id:#{@user.id}") + = link "View all", to: ~p"/comments?#{[cq: "user_id:#{@user.id}"]}" .block__content = for {body, comment} <- @comments, can?(@conn, :show, comment.image) do diff --git a/lib/philomena_web/templates/profile/_recent_galleries.html.slime b/lib/philomena_web/templates/profile/_recent_galleries.html.slime index cbd25cff2..c70e1aba8 100644 --- a/lib/philomena_web/templates/profile/_recent_galleries.html.slime +++ b/lib/philomena_web/templates/profile/_recent_galleries.html.slime @@ -2,7 +2,7 @@ .block .block__header span Recent Galleries - = link "View all", to: Routes.gallery_path(@conn, :index, gallery: [creator: @user.name]) + = link "View all", to: ~p"/galleries?#{[gallery: [creator: @user.name]]}" .media-list = for gallery <- @galleries do diff --git a/lib/philomena_web/templates/profile/_recent_posts.html.slime b/lib/philomena_web/templates/profile/_recent_posts.html.slime index 3d2db2402..5670fb2fe 100644 --- a/lib/philomena_web/templates/profile/_recent_posts.html.slime +++ b/lib/philomena_web/templates/profile/_recent_posts.html.slime @@ -9,10 +9,10 @@ = for post <- @posts do .block__content.alternating-color ' Post - => link pretty_time(post.created_at), to: Routes.forum_topic_path(@conn, :show, post.topic.forum, post.topic, post_id: post) <> "#post_#{post.id}" + => link pretty_time(post.created_at), to: ~p"/forums/#{post.topic.forum}/topics/#{post.topic}?#{[post_id: post]}" <> "#post_#{post.id}" ' in topic - => link post.topic.title, to: Routes.forum_topic_path(@conn, :show, post.topic.forum, post.topic) + => link post.topic.title, to: ~p"/forums/#{post.topic.forum}/topics/#{post.topic}" hr.separator.separator--secondary - a.button href=Routes.post_path(@conn, :index, pq: "user_id:#{@user.id}") + a.button href=~p"/posts?#{[pq: "user_id:#{@user.id}"]}" i.fa.fa-eye> | View all diff --git a/lib/philomena_web/templates/profile/alias/_aliases.html.slime b/lib/philomena_web/templates/profile/alias/_aliases.html.slime index 53913aad7..38030e403 100644 --- a/lib/philomena_web/templates/profile/alias/_aliases.html.slime +++ b/lib/philomena_web/templates/profile/alias/_aliases.html.slime @@ -1,7 +1,7 @@ = for u <- @aliases do tr td - = link u.name, to: Routes.profile_path(@conn, :show, u) + = link u.name, to: ~p"/profiles/#{u}" td = @type diff --git a/lib/philomena_web/templates/profile/artist_link/edit.html.slime b/lib/philomena_web/templates/profile/artist_link/edit.html.slime index 7c643cf48..c03ff4541 100644 --- a/lib/philomena_web/templates/profile/artist_link/edit.html.slime +++ b/lib/philomena_web/templates/profile/artist_link/edit.html.slime @@ -1,2 +1,2 @@ h1 Edit Link -= render PhilomenaWeb.Profile.ArtistLinkView, "_form.html", conn: @conn, changeset: @changeset, tag_name: tag_name(@artist_link), action: Routes.profile_artist_link_path(@conn, :update, @artist_link.user, @artist_link) += render PhilomenaWeb.Profile.ArtistLinkView, "_form.html", conn: @conn, changeset: @changeset, tag_name: tag_name(@artist_link), action: ~p"/profiles/#{@artist_link.user}/artist_links/#{@artist_link}" diff --git a/lib/philomena_web/templates/profile/artist_link/index.html.slime b/lib/philomena_web/templates/profile/artist_link/index.html.slime index d85ac540c..b8e21076d 100644 --- a/lib/philomena_web/templates/profile/artist_link/index.html.slime +++ b/lib/philomena_web/templates/profile/artist_link/index.html.slime @@ -1,6 +1,6 @@ h1 Artist Links p - a.button href=Routes.profile_artist_link_path(@conn, :new, @user) + a.button href=~p"/profiles/#{@user}/artist_links/new" ' Request a link p ' Artist links associate your account on @@ -19,7 +19,7 @@ table.table = for link <- @artist_links do tr td = link link.uri, to: link.uri - td = link "View Details", to: Routes.profile_artist_link_path(@conn, :show, @user, link) + td = link "View Details", to: ~p"/profiles/#{@user}/artist_links/#{link}" td = link.verification_code th = verified_as_string(link) th = public_as_string(link) diff --git a/lib/philomena_web/templates/profile/artist_link/new.html.slime b/lib/philomena_web/templates/profile/artist_link/new.html.slime index 762d9be92..a2566378f 100644 --- a/lib/philomena_web/templates/profile/artist_link/new.html.slime +++ b/lib/philomena_web/templates/profile/artist_link/new.html.slime @@ -1,2 +1,2 @@ h1 Request Artist Link -= render PhilomenaWeb.Profile.ArtistLinkView, "_form.html", changeset: @changeset, action: Routes.profile_artist_link_path(@conn, :create, @user), conn: @conn += render PhilomenaWeb.Profile.ArtistLinkView, "_form.html", changeset: @changeset, action: ~p"/profiles/#{@user}/artist_links", conn: @conn diff --git a/lib/philomena_web/templates/profile/artist_link/show.html.slime b/lib/philomena_web/templates/profile/artist_link/show.html.slime index d38133d46..73aedd3f6 100644 --- a/lib/philomena_web/templates/profile/artist_link/show.html.slime +++ b/lib/philomena_web/templates/profile/artist_link/show.html.slime @@ -54,16 +54,16 @@ h3 Associated tag = if can?(@conn, :index, Philomena.ArtistLinks.ArtistLink) do p - => link "Edit", to: Routes.profile_artist_link_path(@conn, :edit, @user, @artist_link) + => link "Edit", to: ~p"/profiles/#{@user}/artist_links/#{@artist_link}/edit" ' • - => link "Verify", to: Routes.admin_artist_link_verification_path(@conn, :create, @artist_link), method: :post + => link "Verify", to: ~p"/admin/artist_links/#{@artist_link}/verification", method: :post ' • - => link "Reject", to: Routes.admin_artist_link_reject_path(@conn, :create, @artist_link), method: :post + => link "Reject", to: ~p"/admin/artist_links/#{@artist_link}/reject", method: :post = if not verified?(@artist_link) do ' • = if contacted?(@artist_link) do ' Artist contacted - else - = link "Artist contacted", to: Routes.admin_artist_link_contact_path(@conn, :create, @artist_link), method: :post + = link "Artist contacted", to: ~p"/admin/artist_links/#{@artist_link}/contact", method: :post -= link "Back", to: Routes.profile_artist_link_path(@conn, :index, @user) += link "Back", to: ~p"/profiles/#{@user}/artist_links" diff --git a/lib/philomena_web/templates/profile/award/edit.html.slime b/lib/philomena_web/templates/profile/award/edit.html.slime index 0216955ee..94a5f3b6f 100644 --- a/lib/philomena_web/templates/profile/award/edit.html.slime +++ b/lib/philomena_web/templates/profile/award/edit.html.slime @@ -1,3 +1,3 @@ h1 Editing award -= render PhilomenaWeb.Profile.AwardView, "_form.html", changeset: @changeset, badges: @badges, action: Routes.profile_award_path(@conn, :update, @user, @award), conn: @conn += render PhilomenaWeb.Profile.AwardView, "_form.html", changeset: @changeset, badges: @badges, action: ~p"/profiles/#{@user}/awards/#{@award}", conn: @conn diff --git a/lib/philomena_web/templates/profile/award/new.html.slime b/lib/philomena_web/templates/profile/award/new.html.slime index 489bc93e6..525e06cfe 100644 --- a/lib/philomena_web/templates/profile/award/new.html.slime +++ b/lib/philomena_web/templates/profile/award/new.html.slime @@ -1,3 +1,3 @@ h1 New award -= render PhilomenaWeb.Profile.AwardView, "_form.html", changeset: @changeset, badges: @badges, action: Routes.profile_award_path(@conn, :create, @user), conn: @conn += render PhilomenaWeb.Profile.AwardView, "_form.html", changeset: @changeset, badges: @badges, action: ~p"/profiles/#{@user}/awards", conn: @conn diff --git a/lib/philomena_web/templates/profile/commission/_listing_items.html.slime b/lib/philomena_web/templates/profile/commission/_listing_items.html.slime index dfb66ae14..15502ebb0 100644 --- a/lib/philomena_web/templates/profile/commission/_listing_items.html.slime +++ b/lib/philomena_web/templates/profile/commission/_listing_items.html.slime @@ -3,7 +3,7 @@ span Available Items and Prices = if current?(@user, @conn.assigns.current_user) do - = link "Add an item", to: Routes.profile_commission_item_path(@conn, :new, @user) + = link "Add an item", to: ~p"/profiles/#{@user}/commission/items/new" .block__content @@ -12,7 +12,7 @@ p You have not added any items to your commissions sheet yet. p ' Your listing will not appear in search results until you - = link "list at least one item", to: Routes.profile_commission_item_path(@conn, :new, @user) + = link "list at least one item", to: ~p"/profiles/#{@user}/commission/items/new" ' . - else p This artist has not added any items yet. Please check back later. @@ -52,7 +52,7 @@ = if can?(@conn, :edit, @commission) do td - = link "Edit item", to: Routes.profile_commission_item_path(@conn, :edit, @user, item) + = link "Edit item", to: ~p"/profiles/#{@user}/commission/items/#{item}/edit" br br - = link "Delete item", to: Routes.profile_commission_item_path(@conn, :delete, @user, item), data: [confirm: "Are you really, really sure?", method: "delete"] + = link "Delete item", to: ~p"/profiles/#{@user}/commission/items/#{item}", data: [confirm: "Are you really, really sure?", method: "delete"] diff --git a/lib/philomena_web/templates/profile/commission/_listing_sidebar.html.slime b/lib/philomena_web/templates/profile/commission/_listing_sidebar.html.slime index 545d31e23..6c0d81fe5 100644 --- a/lib/philomena_web/templates/profile/commission/_listing_sidebar.html.slime +++ b/lib/philomena_web/templates/profile/commission/_listing_sidebar.html.slime @@ -71,18 +71,18 @@ span Options .block__content = if can?(@conn, :edit, @commission) do - = link "Edit this listing", to: Routes.profile_commission_path(@conn, :edit, @user) + = link "Edit this listing", to: ~p"/profiles/#{@user}/commission/edit" br - = link "Delete this listing", to: Routes.profile_commission_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure about that?", method: "delete"] + = link "Delete this listing", to: ~p"/profiles/#{@user}/commission", data: [confirm: "Are you really, really sure about that?", method: "delete"] br - = link "Report this listing", to: Routes.profile_commission_report_path(@conn, :new, @user) + = link "Report this listing", to: ~p"/profiles/#{@user}/commission/reports/new" / Share block .block .block__header span Share this listing .block__content - - url = Routes.profile_commission_url(@conn, :show, @user) + - url = url(~p"/profiles/#{@user}/commission") .field label> for="commission_url" URL diff --git a/lib/philomena_web/templates/profile/commission/edit.html.slime b/lib/philomena_web/templates/profile/commission/edit.html.slime index aa6bab716..514d03f0e 100644 --- a/lib/philomena_web/templates/profile/commission/edit.html.slime +++ b/lib/philomena_web/templates/profile/commission/edit.html.slime @@ -1,5 +1,5 @@ h1 Edit Commission Listing p - = link "Back to commission", to: Routes.profile_commission_path(@conn, :show, @user) + = link "Back to commission", to: ~p"/profiles/#{@user}/commission" -= render PhilomenaWeb.Profile.CommissionView, "_form.html", changeset: @changeset, action: Routes.profile_commission_path(@conn, :update, @user), conn: @conn \ No newline at end of file += render PhilomenaWeb.Profile.CommissionView, "_form.html", changeset: @changeset, action: ~p"/profiles/#{@user}/commission", conn: @conn \ No newline at end of file diff --git a/lib/philomena_web/templates/profile/commission/item/edit.html.slime b/lib/philomena_web/templates/profile/commission/item/edit.html.slime index cb8124be1..46d4e28b2 100644 --- a/lib/philomena_web/templates/profile/commission/item/edit.html.slime +++ b/lib/philomena_web/templates/profile/commission/item/edit.html.slime @@ -1,5 +1,5 @@ h1 Edit Item on Listing p - = link "Back to listing", to: Routes.profile_commission_path(@conn, :show, @user) + = link "Back to listing", to: ~p"/profiles/#{@user}/commission" -= render PhilomenaWeb.Profile.Commission.ItemView, "_form.html", conn: @conn, changeset: @changeset, action: Routes.profile_commission_item_path(@conn, :update, @user, @item) += render PhilomenaWeb.Profile.Commission.ItemView, "_form.html", conn: @conn, changeset: @changeset, action: ~p"/profiles/#{@user}/commission/items/#{@item}" diff --git a/lib/philomena_web/templates/profile/commission/item/new.html.slime b/lib/philomena_web/templates/profile/commission/item/new.html.slime index b2a756904..6897a066e 100644 --- a/lib/philomena_web/templates/profile/commission/item/new.html.slime +++ b/lib/philomena_web/templates/profile/commission/item/new.html.slime @@ -1,5 +1,5 @@ h1 New Item on Listing p - = link "Back to listing", to: Routes.profile_commission_path(@conn, :show, @user) + = link "Back to listing", to: ~p"/profiles/#{@user}/commission" -= render PhilomenaWeb.Profile.Commission.ItemView, "_form.html", conn: @conn, changeset: @changeset, action: Routes.profile_commission_item_path(@conn, :create, @user) += render PhilomenaWeb.Profile.Commission.ItemView, "_form.html", conn: @conn, changeset: @changeset, action: ~p"/profiles/#{@user}/commission/items" diff --git a/lib/philomena_web/templates/profile/commission/new.html.slime b/lib/philomena_web/templates/profile/commission/new.html.slime index 98653d8dd..0e08e263c 100644 --- a/lib/philomena_web/templates/profile/commission/new.html.slime +++ b/lib/philomena_web/templates/profile/commission/new.html.slime @@ -1,5 +1,5 @@ h1 New Commission Listing p - = link "Back to index", to: Routes.commission_path(@conn, :index) + = link "Back to index", to: ~p"/commissions" -= render PhilomenaWeb.Profile.CommissionView, "_form.html", changeset: @changeset, action: Routes.profile_commission_path(@conn, :create, @user), conn: @conn \ No newline at end of file += render PhilomenaWeb.Profile.CommissionView, "_form.html", changeset: @changeset, action: ~p"/profiles/#{@user}/commission", conn: @conn \ No newline at end of file diff --git a/lib/philomena_web/templates/profile/description/edit.html.slime b/lib/philomena_web/templates/profile/description/edit.html.slime index 9e5cddb7c..5174098cf 100644 --- a/lib/philomena_web/templates/profile/description/edit.html.slime +++ b/lib/philomena_web/templates/profile/description/edit.html.slime @@ -1,6 +1,6 @@ h1 Updating Profile Description -= form_for @changeset, Routes.profile_description_path(@conn, :update, @user), [method: "put"], fn f -> += form_for @changeset, ~p"/profiles/#{@user}/description", [method: "put"], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/profile/detail/index.html.slime b/lib/philomena_web/templates/profile/detail/index.html.slime index 2384a8552..7d51cdfc3 100644 --- a/lib/philomena_web/templates/profile/detail/index.html.slime +++ b/lib/philomena_web/templates/profile/detail/index.html.slime @@ -1,10 +1,10 @@ h2 - = link @user.name, to: Routes.profile_path(@conn, :show, @user) + = link @user.name, to: ~p"/profiles/#{@user}" | 's User Details h4 Mod Notes = render PhilomenaWeb.Admin.ModNoteView, "_table.html", mod_notes: @mod_notes, conn: @conn -= link "Add New Note", to: Routes.admin_mod_note_path(@conn, :new, notable_id: @user.id, notable_type: "User") += link "Add New Note", to: ~p"/admin/mod_notes/new?#{[notable_id: @user.id, notable_type: "User"]}" h4 Name History table.table @@ -20,6 +20,6 @@ table.table h4 More Details ul - li = link "IP Address Usage History", to: Routes.profile_ip_history_path(@conn, :index, @user) - li = link "Fingerprint Usage History", to: Routes.profile_fp_history_path(@conn, :index, @user) - li = link "Potential Aliases", to: Routes.profile_alias_path(@conn, :index, @user) + li = link "IP Address Usage History", to: ~p"/profiles/#{@user}/ip_history" + li = link "Fingerprint Usage History", to: ~p"/profiles/#{@user}/fp_history" + li = link "Potential Aliases", to: ~p"/profiles/#{@user}/aliases" diff --git a/lib/philomena_web/templates/profile/fp_history/index.html.slime b/lib/philomena_web/templates/profile/fp_history/index.html.slime index b67a9e3c8..182c30c58 100644 --- a/lib/philomena_web/templates/profile/fp_history/index.html.slime +++ b/lib/philomena_web/templates/profile/fp_history/index.html.slime @@ -10,7 +10,7 @@ ul ul = for u <- @other_users[ufp.fingerprint] do li - => link u.user.name, to: Routes.profile_path(@conn, :show, u.user) + => link u.user.name, to: ~p"/profiles/#{u.user}" | ( => u.uses ' uses, last used diff --git a/lib/philomena_web/templates/profile/ip_history/index.html.slime b/lib/philomena_web/templates/profile/ip_history/index.html.slime index 3d494d787..d5ffc0ffa 100644 --- a/lib/philomena_web/templates/profile/ip_history/index.html.slime +++ b/lib/philomena_web/templates/profile/ip_history/index.html.slime @@ -10,7 +10,7 @@ ul ul = for u <- @other_users[uip.ip] do li - => link u.user.name, to: Routes.profile_path(@conn, :show, u.user) + => link u.user.name, to: ~p"/profiles/#{u.user}" | ( => u.uses ' uses, last used diff --git a/lib/philomena_web/templates/profile/scratchpad/edit.html.slime b/lib/philomena_web/templates/profile/scratchpad/edit.html.slime index a1eca8f5f..fd1fea171 100644 --- a/lib/philomena_web/templates/profile/scratchpad/edit.html.slime +++ b/lib/philomena_web/templates/profile/scratchpad/edit.html.slime @@ -1,6 +1,6 @@ h1 Updating Moderation Scratchpad -= form_for @changeset, Routes.profile_scratchpad_path(@conn, :update, @user), [method: "put"], fn f -> += form_for @changeset, ~p"/profiles/#{@user}/scratchpad", [method: "put"], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/profile/show.html.slime b/lib/philomena_web/templates/profile/show.html.slime index 4157abb3a..81f68c3c6 100644 --- a/lib/philomena_web/templates/profile/show.html.slime +++ b/lib/philomena_web/templates/profile/show.html.slime @@ -2,7 +2,7 @@ .profile-top__avatar - avatar = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @user}, class: "avatar--large" = if current?(@user, @conn.assigns.current_user) do - = link avatar, to: Routes.avatar_path(@conn, :edit, profile: true), title: "Change avatar" + = link avatar, to: ~p"/avatar/edit?#{[profile: true]}", title: "Change avatar" - else = avatar .profile-top__name-and-links @@ -14,7 +14,7 @@ br = if can?(@conn, :edit_description, @user) do - = link "Edit Personal Title", to: Routes.profile_description_path(@conn, :edit, @user) + = link "Edit Personal Title", to: ~p"/profiles/#{@user}/description/edit" br span @@ -23,21 +23,21 @@ .profile-top__options ul.profile-top__options__column - li = link("Send message", to: Routes.conversation_path(@conn, :new, recipient: @user.name)) - li = link("Our conversations", to: Routes.conversation_path(@conn, :index, with: @user.id)) - li = link("Report this user", to: Routes.profile_report_path(@conn, :new, @user)) + li = link("Send message", to: ~p"/conversations/new?#{[recipient: @user.name]}") + li = link("Our conversations", to: ~p"/conversations?#{[with: @user.id]}") + li = link("Report this user", to: ~p"/profiles/#{@user}/reports/new") ul.profile-top__options__column - li = link("Uploads", to: Routes.search_path(@conn, :index, q: "uploader_id:#{@user.id}")) - li = link("Comments", to: Routes.comment_path(@conn, :index, cq: "user_id:#{@user.id}")) - li = link("Posts", to: Routes.post_path(@conn, :index, pq: "user_id:#{@user.id}")) + li = link("Uploads", to: ~p"/search?#{[q: "uploader_id:#{@user.id}"]}") + li = link("Comments", to: ~p"/comments?#{[cq: "user_id:#{@user.id}"]}") + li = link("Posts", to: ~p"/posts?#{[pq: "user_id:#{@user.id}"]}") = if current?(@user, @conn.assigns.current_user) do - li = link "My reports", to: Routes.report_path(@conn, :index) + li = link "My reports", to: ~p"/reports" ul.profile-top__options__column - li = link("Favorites", to: Routes.search_path(@conn, :index, q: "faved_by_id:#{@user.id}")) - li = link("Tag changes", to: Routes.profile_tag_change_path(@conn, :index, @user)) - li = link("Source changes", to: Routes.profile_source_change_path(@conn, :index, @user)) + li = link("Favorites", to: ~p"/search?#{[q: "faved_by_id:#{@user.id}"]}") + li = link("Tag changes", to: ~p"/profiles/#{@user}/tag_changes") + li = link("Source changes", to: ~p"/profiles/#{@user}/source_changes") = if can_index_user?(@conn) do .js-staff-action @@ -47,7 +47,7 @@ .block i.fa.icon--padded.small.fa-filter> strong.comment_deleted> Forced Filter: - = link @forced.name, to: Routes.filter_path(@conn, :show, @forced) + = link @forced.name, to: ~p"/filters/#{@forced}" = if (current?(@user, @conn.assigns.current_user) or can?(@conn, :index, Philomena.Bans.User)) and Enum.any?(@bans) do .block.block--warning @@ -92,10 +92,10 @@ - else ' Hidden ' • - a href=Routes.profile_artist_link_path(@conn, :edit, @user, link) + a href=~p"/profiles/#{@user}/artist_links/#{link}/edit" ' Edit ' • - a href=Routes.admin_artist_link_reject_path(@conn, :create, link) data-method="post" + a href=~p"/admin/artist_links/#{link}/reject" data-method="post" ' Reject - else => unless link.public do @@ -104,7 +104,7 @@ = if current?(@user, @conn.assigns.current_user) or manages_links?(@conn, @user) do .block__content.alternating-color.break-word - a.button href=Routes.profile_artist_link_path(@conn, :new, @user) + a.button href=~p"/profiles/#{@user}/artist_links/new" i.fa.fa-link> | Create an Artist Link .block @@ -120,18 +120,18 @@ .flex__grow.center => pretty_time(award.awarded_on) = if manages_awards?(@conn) do - = user_abbrv(@conn, award.awarded_by) + = user_abbrv(award.awarded_by) = if manages_awards?(@conn) do .flex__grow.center - a href=Routes.profile_award_path(@conn, :delete, @user, award) data-method="delete" data-confirm="Are you really, really sure?" + a href=~p"/profiles/#{@user}/awards/#{award}" data-method="delete" data-confirm="Are you really, really sure?" ' Remove br - a href=Routes.profile_award_path(@conn, :edit, @user, award) + a href=~p"/profiles/#{@user}/awards/#{award}/edit" ' Edit = if manages_awards?(@conn) and not hide_staff_tools?(@conn) do .block__content.alternating-color.no-overflow - a.button href=Routes.profile_award_path(@conn, :new, @user) + a.button href=~p"/profiles/#{@user}/awards/new" i.fa.fa-trophy> | Award a badge @@ -158,7 +158,7 @@ td = body td = pretty_time(mod_note.created_at) .block__content - a.button href=Routes.profile_detail_path(@conn, :index, @user) + a.button href=~p"/profiles/#{@user}/details" i.fa.fa-plus> | Edit mod notes = if can_index_user?(@conn) do @@ -169,15 +169,15 @@ .block__content.profile-about = @scratchpad .block__content - a.button href=Routes.profile_scratchpad_path(@conn, :edit, @user) + a.button href=~p"/profiles/#{@user}/scratchpad/edit" i.fa.fa-edit> | Edit .column-layout__main = render PhilomenaWeb.ProfileView, "_statistics.html", user: @user, statistics: @statistics, conn: @conn - = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Artwork", images: @recent_artwork, view_all_path: Routes.search_path(@conn, :index, q: tag_disjunction(@tags)), conn: @conn - = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Uploads", images: @recent_uploads, view_all_path: Routes.search_path(@conn, :index, q: "uploader_id:#{@user.id}"), conn: @conn - = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Favorites", images: @recent_faves, view_all_path: Routes.search_path(@conn, :index, q: "faved_by_id:#{@user.id}"), conn: @conn + = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Artwork", images: @recent_artwork, view_all_path: ~p"/search?#{[q: tag_disjunction(@tags)]}", conn: @conn + = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Uploads", images: @recent_uploads, view_all_path: ~p"/search?#{[q: "uploader_id:#{@user.id}"]}", conn: @conn + = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Favorites", images: @recent_faves, view_all_path: ~p"/search?#{[q: "faved_by_id:#{@user.id}"]}", conn: @conn = render PhilomenaWeb.ProfileView, "_recent_galleries.html", galleries: @recent_galleries, user: @user, conn: @conn = render PhilomenaWeb.ProfileView, "_recent_comments.html", comments: @recent_comments, user: @user, conn: @conn = render PhilomenaWeb.ProfileView, "_recent_posts.html", posts: @recent_posts, user: @user, conn: @conn diff --git a/lib/philomena_web/templates/profile/source_change/index.html.slime b/lib/philomena_web/templates/profile/source_change/index.html.slime index 906a2820e..d03fb4f5f 100644 --- a/lib/philomena_web/templates/profile/source_change/index.html.slime +++ b/lib/philomena_web/templates/profile/source_change/index.html.slime @@ -1,9 +1,9 @@ h1 ' Source changes by - a href=Routes.profile_path(@conn, :show, @user) + a href=~p"/profiles/#{@user}" = @user.name -- route = fn p -> Routes.profile_source_change_path(@conn, :index, @user, p) end +- route = fn p -> ~p"/profiles/#{@user}/source_changes?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @source_changes, route: route, conn: @conn = render PhilomenaWeb.SourceChangeView, "index.html", conn: @conn, source_changes: @source_changes, pagination: pagination \ No newline at end of file diff --git a/lib/philomena_web/templates/profile/tag_change/index.html.slime b/lib/philomena_web/templates/profile/tag_change/index.html.slime index 806d843a1..0cbc97dd7 100644 --- a/lib/philomena_web/templates/profile/tag_change/index.html.slime +++ b/lib/philomena_web/templates/profile/tag_change/index.html.slime @@ -1,20 +1,20 @@ h1 ' Tag changes by - a href=Routes.profile_path(@conn, :show, @user) + a href=~p"/profiles/#{@user}" = @user.name -- route = fn p -> Routes.profile_tag_change_path(@conn, :index, @user, p) end +- route = fn p -> ~p"/profiles/#{@user}/tag_changes?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @tag_changes, route: route, conn: @conn, params: @pagination_params .block .block__header - = form_for @conn, Routes.profile_tag_change_path(@conn, :index, @user), [method: "get", enforce_utf8: false], fn f -> + = form_for @conn, ~p"/profiles/#{@user}/tag_changes", [method: "get", enforce_utf8: false], fn f -> = text_input f, :only_tag, class: "input", placeholder: "Tag", title: "Only show this tag", autocapitalize: "none" = submit "Search", class: "button", title: "Search" - = link "Removed", to: Routes.profile_tag_change_path(@conn, :index, @user, Keyword.merge(@pagination_params, added: 0)) - = link "Added", to: Routes.profile_tag_change_path(@conn, :index, @user, Keyword.merge(@pagination_params, added: 1)) - = link "All", to: Routes.profile_tag_change_path(@conn, :index, @user, Keyword.delete(@pagination_params, :added)) + = link "Removed", to: ~p"/profiles/#{@user}/tag_changes?#{Keyword.merge(@pagination_params, added: 0)}" + = link "Added", to: ~p"/profiles/#{@user}/tag_changes?#{Keyword.merge(@pagination_params, added: 1)}" + = link "All", to: ~p"/profiles/#{@user}/tag_changes?#{Keyword.delete(@pagination_params, :added)}" .block__header.block__header--light span.block__header__title.page__info diff --git a/lib/philomena_web/templates/registration/edit.html.slime b/lib/philomena_web/templates/registration/edit.html.slime index 88bfd1aae..a0134b6eb 100644 --- a/lib/philomena_web/templates/registration/edit.html.slime +++ b/lib/philomena_web/templates/registration/edit.html.slime @@ -6,16 +6,16 @@ p p ' Looking for two factor authentication? - = link "Click here!", to: Routes.registration_totp_path(@conn, :edit) + = link "Click here!", to: ~p"/registrations/totp/edit" p ' Looking to change your avatar? - = link "Click here!", to: Routes.avatar_path(@conn, :edit) + = link "Click here!", to: ~p"/avatar/edit" = if can?(@conn, :change_username, @current_user) do p ' Looking to change your username? - = link "Click here!", to: Routes.registration_name_path(@conn, :edit) + = link "Click here!", to: ~p"/registrations/name/edit" h3 API Key p @@ -33,7 +33,7 @@ p h3 Change email -= form_for @email_changeset, Routes.registration_email_path(@conn, :create), [method: :post], fn f -> += form_for @email_changeset, ~p"/registrations/email", [method: :post], fn f -> = if @email_changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -51,7 +51,7 @@ h3 Change email h3 Change password -= form_for @password_changeset, Routes.registration_password_path(@conn, :update), fn f -> += form_for @password_changeset, ~p"/registrations/password", fn f -> = if @password_changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/registration/name/edit.html.slime b/lib/philomena_web/templates/registration/name/edit.html.slime index 529b57a1d..1f9bce281 100644 --- a/lib/philomena_web/templates/registration/name/edit.html.slime +++ b/lib/philomena_web/templates/registration/name/edit.html.slime @@ -1,6 +1,6 @@ h1 Editing Name -= form_for @changeset, Routes.registration_name_path(@conn, :update), [as: :user], fn f -> += form_for @changeset, ~p"/registrations/name", [as: :user], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -14,4 +14,4 @@ h1 Editing Name .action = submit "Save", class: "button" -p = link "Back", to: Routes.registration_path(@conn, :edit) +p = link "Back", to: ~p"/registrations/edit" diff --git a/lib/philomena_web/templates/registration/new.html.slime b/lib/philomena_web/templates/registration/new.html.slime index 99e49ab6a..82d8b8d52 100644 --- a/lib/philomena_web/templates/registration/new.html.slime +++ b/lib/philomena_web/templates/registration/new.html.slime @@ -1,6 +1,6 @@ h1 Register -= form_for @changeset, Routes.registration_path(@conn, :create), fn f -> += form_for @changeset, ~p"/registrations", fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/registration/totp/edit.html.slime b/lib/philomena_web/templates/registration/totp/edit.html.slime index 9c95f2f80..8fd3028b9 100644 --- a/lib/philomena_web/templates/registration/totp/edit.html.slime +++ b/lib/philomena_web/templates/registration/totp/edit.html.slime @@ -1,6 +1,6 @@ h1 Two Factor Authentication -= form_for @changeset, Routes.registration_totp_path(@conn, :update), [as: :user], fn f -> += form_for @changeset, ~p"/registrations/totp", [as: :user], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -133,4 +133,4 @@ h1 Two Factor Authentication = submit "Save Account", class: "button" -p = link "Back", to: Routes.registration_path(@conn, :edit) +p = link "Back", to: ~p"/registrations/edit" diff --git a/lib/philomena_web/templates/report/index.html.slime b/lib/philomena_web/templates/report/index.html.slime index cbc9c191d..5c25d6766 100644 --- a/lib/philomena_web/templates/report/index.html.slime +++ b/lib/philomena_web/templates/report/index.html.slime @@ -2,7 +2,7 @@ h1 Your Reports .block .block__header span Reports - - route = fn p -> Routes.report_path(@conn, :index, p) end + - route = fn p -> ~p"/reports?#{p}" end = render PhilomenaWeb.PaginationView, "_pagination.html", page: @reports, route: route, conn: @conn .block__content @@ -18,6 +18,6 @@ h1 Your Reports tr td class=report_row_class(r) = pretty_state(r) - td = link_to_reported_thing(@conn, r.reportable) + td = link_to_reported_thing(r.reportable) td = r.reason td = pretty_time(r.created_at) diff --git a/lib/philomena_web/templates/report/new.html.slime b/lib/philomena_web/templates/report/new.html.slime index 15f2a32a6..f78397834 100644 --- a/lib/philomena_web/templates/report/new.html.slime +++ b/lib/philomena_web/templates/report/new.html.slime @@ -1,7 +1,7 @@ h2 Submit a report p strong - = link_to_reported_thing(@conn, @reportable) + = link_to_reported_thing(@reportable) .image-other .dnp-warning diff --git a/lib/philomena_web/templates/search/_form.html.slime b/lib/philomena_web/templates/search/_form.html.slime index 1569f2001..d1ceb34dd 100644 --- a/lib/philomena_web/templates/search/_form.html.slime +++ b/lib/philomena_web/templates/search/_form.html.slime @@ -1,6 +1,6 @@ h1 Search -= form_for :search, Routes.search_path(@conn, :index), [id: "searchform", method: "get", class: "js-search-form", enforce_utf8: false], fn f -> += form_for :search, ~p"/search", [id: "searchform", method: "get", class: "js-search-form", enforce_utf8: false], fn f -> = text_input f, :q, class: "input input--wide js-search-field", placeholder: "Search terms are chained with commas", autocapitalize: "none", name: "q", value: @conn.params["q"] .block diff --git a/lib/philomena_web/templates/search/index.html.slime b/lib/philomena_web/templates/search/index.html.slime index 2c4cffe98..130d7dd41 100644 --- a/lib/philomena_web/templates/search/index.html.slime +++ b/lib/philomena_web/templates/search/index.html.slime @@ -1,6 +1,6 @@ = cond do - Enum.any?(@images) or override_display(@tags) -> - = render PhilomenaWeb.ImageView, "index.html", conn: @conn, tags: @tags, images: @images, header: "Searching for #{@conn.params["q"]}", route: fn p -> Routes.search_path(@conn, :index, p) end, scope: scope(@conn) + = render PhilomenaWeb.ImageView, "index.html", conn: @conn, tags: @tags, images: @images, header: "Searching for #{@conn.params["q"]}", route: fn p -> ~p"/search?#{p}" end, scope: scope(@conn) - assigns[:error] -> .block.block--fixed.block--danger diff --git a/lib/philomena_web/templates/search/reverse/index.html.slime b/lib/philomena_web/templates/search/reverse/index.html.slime index 9d8db2c3b..6ae7b2d7b 100644 --- a/lib/philomena_web/templates/search/reverse/index.html.slime +++ b/lib/philomena_web/templates/search/reverse/index.html.slime @@ -1,6 +1,6 @@ h1 Reverse Search -= form_for :image, Routes.search_reverse_path(@conn, :create), [multipart: true], fn f -> += form_for :image, ~p"/search/reverse", [multipart: true], fn f -> p ' Basic image similarity search. Finds uploaded images similar to the one ' provided based on simple intensities and uses the median frame of @@ -46,7 +46,7 @@ h1 Reverse Search = for match <- @images do tr th - h3 = link "##{match.id}", to: Routes.image_path(@conn, :show, match) + h3 = link "##{match.id}", to: ~p"/images/#{match}" p = if image_has_sources(match) do span.source_url diff --git a/lib/philomena_web/templates/session/new.html.slime b/lib/philomena_web/templates/session/new.html.slime index a8fd640cc..11a86ae9a 100644 --- a/lib/philomena_web/templates/session/new.html.slime +++ b/lib/philomena_web/templates/session/new.html.slime @@ -1,13 +1,13 @@ h1 Sign in -= form_for @conn, Routes.session_path(@conn, :create), [as: :user], fn f -> += form_for @conn, ~p"/sessions", [as: :user], fn f -> = if @error_message do .alert.alert-danger p = @error_message - p = link "Resend unlock instructions", to: Routes.unlock_path(@conn, :new) - p = link "Resend confirmation email", to: Routes.confirmation_path(@conn, :new) + p = link "Resend unlock instructions", to: ~p"/unlocks/new" + p = link "Resend confirmation email", to: ~p"/confirmations/new" - p = link "Forgot your password?", to: Routes.password_path(@conn, :new) + p = link "Forgot your password?", to: ~p"/passwords/new" .field = email_input f, :email, class: "input", required: true, placeholder: "Email", autofocus: true, pattern: ~S/[^\s]+@[^\s]+\.[^\s]+/ diff --git a/lib/philomena_web/templates/session/totp/new.html.slime b/lib/philomena_web/templates/session/totp/new.html.slime index 0c3bf9ebd..0a632f9fb 100644 --- a/lib/philomena_web/templates/session/totp/new.html.slime +++ b/lib/philomena_web/templates/session/totp/new.html.slime @@ -1,6 +1,6 @@ h1 Two Factor Authentication -= form_for @changeset, Routes.session_totp_path(@conn, :create), [as: :user, method: "post"], fn f -> += form_for @changeset, ~p"/sessions/totp", [as: :user, method: "post"], fn f -> .field h4 Please enter your 2FA code = text_input f, :twofactor_token, class: "input", placeholder: "6-digit code", required: true, autofocus: true, autocomplete: "off" diff --git a/lib/philomena_web/templates/setting/edit.html.slime b/lib/philomena_web/templates/setting/edit.html.slime index cce9bdf3f..2da58c651 100644 --- a/lib/philomena_web/templates/setting/edit.html.slime +++ b/lib/philomena_web/templates/setting/edit.html.slime @@ -1,5 +1,5 @@ h1 Content Settings -= form_for @changeset, Routes.setting_path(@conn, :update), [method: "put"], fn f -> += form_for @changeset, ~p"/settings", [method: "put"], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -51,7 +51,7 @@ h1 Content Settings code> = link("Click to show", to: "#", data: [click_show: "#rss-link", click_hide: "#rss-feed-button"]) #rss-link.hidden - = url_input f, :subscribe_url, value: Routes.api_rss_watched_url(@conn, :index, key: @conn.assigns.current_user.authentication_token), class: "input input--wide" + = url_input f, :subscribe_url, value: url(~p"/api/v1/rss/watched?#{[key: @conn.assigns.current_user.authentication_token]}"), class: "input input--wide" br ' Do not share this URL with anyone, it may allow an attacker to compromise your account. @@ -90,13 +90,13 @@ h1 Content Settings .field#js-theme-dark label Theme color .with-error - => select f, :dark_theme, theme_options(@conn), class: "input" + => select f, :dark_theme, theme_options(), class: "input" = error_tag f, :dark_theme .fieldlabel: i Color of the theme, don't forget to save settings to apply the theme .field.hidden#js-theme-light label Theme color .with-error - => select f, :light_theme, light_theme_options(@conn), class: "input" + => select f, :light_theme, light_theme_options(), class: "input" = error_tag f, :light_theme .fieldlabel: i Preview themes by selecting one from the dropdown. Saving sets the currently selected theme. .field @@ -202,7 +202,7 @@ h1 Content Settings .block__tab.hidden data-tab="join-the-herd" p ' Consider - => link "creating an account!", to: Routes.registration_path(@conn, :new) + => link "creating an account!", to: ~p"/registrations/new" br ' You will be able to customize the number of images and comments you get on a single page, as well as change the appearance of the site with custom themes. diff --git a/lib/philomena_web/templates/source_change/index.html.slime b/lib/philomena_web/templates/source_change/index.html.slime index 05971a763..fe4b37b24 100644 --- a/lib/philomena_web/templates/source_change/index.html.slime +++ b/lib/philomena_web/templates/source_change/index.html.slime @@ -16,7 +16,7 @@ = for source_change <- @source_changes do tr td.center - = link source_change.image_id, to: Routes.image_path(@conn, :show, source_change.image) + = link source_change.image_id, to: ~p"/images/#{source_change.image}" td.center = render PhilomenaWeb.ImageView, "_image_container.html", image: source_change.image, size: :thumb_tiny, conn: @conn diff --git a/lib/philomena_web/templates/staff/index.html.slime b/lib/philomena_web/templates/staff/index.html.slime index dc7de958e..548280276 100644 --- a/lib/philomena_web/templates/staff/index.html.slime +++ b/lib/philomena_web/templates/staff/index.html.slime @@ -39,12 +39,12 @@ h1 Staff .block__content.staff-block__user .staff-block__user-card .staff-block__avatar - a.profile-block href=Routes.profile_path(@conn, :show, user) + a.profile-block href=~p"/profiles/#{user}" = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: user}, class: "avatar--large" p b = user.name .staff-block__info - = link to: Routes.conversation_path(@conn, :new, recipient: user.name), class: "button button--secondary" do + = link to: ~p"/conversations/new?#{[recipient: user.name]}", class: "button button--secondary" do i.fa.fa-envelope> ' Send PM hr.staff-block__separator diff --git a/lib/philomena_web/templates/tag/_tag.html.slime b/lib/philomena_web/templates/tag/_tag.html.slime index 4bc778827..0de59b7ee 100644 --- a/lib/philomena_web/templates/tag/_tag.html.slime +++ b/lib/philomena_web/templates/tag/_tag.html.slime @@ -13,14 +13,14 @@ span.tag.dropdown data-tag-category="#{@tag.category}" data-tag-id="#{@tag.id}" a.tag__name< href=pretty_tag_path(@tag) title="#{@tag.short_description}" = @tag.name div.dropdown__content - a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unwatch" href=Routes.tag_watch_path(@conn, :delete, @tag) Unwatch - a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="watch" href=Routes.tag_watch_path(@conn, :create, @tag) Watch + a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unwatch" href=~p"/tags/#{@tag}/watch" Unwatch + a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="watch" href=~p"/tags/#{@tag}/watch" Watch - a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unspoiler" href=Routes.filter_spoiler_path(@conn, :delete, tag: @tag) Unspoiler - a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="spoiler" href=Routes.filter_spoiler_path(@conn, :create, tag: @tag) Spoiler + a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unspoiler" href=~p"/filters/spoiler?#{[tag: @tag]}" Unspoiler + a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="spoiler" href=~p"/filters/spoiler?#{[tag: @tag]}" Spoiler - a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unhide" href=Routes.filter_hide_path(@conn, :delete, tag: @tag) Unhide - a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="hide" href=Routes.filter_hide_path(@conn, :create, tag: @tag) Hide + a.tag__dropdown__link.hidden data-method="delete" data-remote="true" data-tag-action="unhide" href=~p"/filters/hide?#{[tag: @tag]}" Unhide + a.tag__dropdown__link.hidden data-method="post" data-remote="true" data-tag-action="hide" href=~p"/filters/hide?#{[tag: @tag]}" Hide a.tag__dropdown__link.hidden href="/sessions/new" Sign in to Watch a.tag__dropdown__link.hidden href="/filters" Filter diff --git a/lib/philomena_web/templates/tag/_tag_info_row.html.slime b/lib/philomena_web/templates/tag/_tag_info_row.html.slime index 766420f64..8444c697e 100644 --- a/lib/philomena_web/templates/tag/_tag_info_row.html.slime +++ b/lib/philomena_web/templates/tag/_tag_info_row.html.slime @@ -9,15 +9,15 @@ .tag-info__controls = render PhilomenaWeb.TagView, "_tag.html", tag: @tag, conn: @conn .button__group--standalone - = link "Tag changes", to: Routes.tag_tag_change_path(@conn, :index, @tag), class: "detail-link" + = link "Tag changes", to: ~p"/tags/#{@tag}/tag_changes", class: "detail-link" = if manages_tags?(@conn) do .button__group--standalone.button__group--warning - = link "Edit details", to: Routes.tag_path(@conn, :edit, @tag), class: "detail-link" + = link "Edit details", to: ~p"/tags/#{@tag}/edit", class: "detail-link" .separator--vertical.separator--warning - = link "Usage", to: Routes.tag_detail_path(@conn, :index, @tag), class: "detail-link" + = link "Usage", to: ~p"/tags/#{@tag}/details", class: "detail-link" = if manages_dnp?(@conn) do .button__group--standalone.button__group--danger - = link "Create new DNP entry", to: Routes.dnp_entry_path(@conn, :new, tag_id: @tag.id), class: "detail-link" + = link "Create new DNP entry", to: ~p"/dnp/new?#{[tag_id: @tag.id]}", class: "detail-link" br @@ -34,14 +34,14 @@ = if Enum.any?(@tag.aliases) do strong> Aliases: = if aliases_tags?(@conn) do - = map_join(@tag.aliases, ", ", &link(&1.name, to: Routes.tag_alias_path(@conn, :edit, &1))) + = map_join(@tag.aliases, ", ", &link(&1.name, to: ~p"/tags/#{&1}/alias/edit")) - else = map_join(@tag.aliases, ", ", & &1.name) br = if Enum.any?(@tag.implied_tags) do strong> Implies: - = map_join(@tag.implied_tags, ", ", &link(&1.name, to: Routes.tag_path(@conn, :show, &1))) + = map_join(@tag.implied_tags, ", ", &link(&1.name, to: ~p"/tags/#{&1}")) br = if Enum.any?(@tag.hidden_links) and manages_links?(@conn) do @@ -49,7 +49,7 @@ br = for artist_link <- @tag.hidden_links do - => link artist_link.user.name, to: Routes.profile_path(@conn, :show, artist_link.user) + => link artist_link.user.name, to: ~p"/profiles/#{artist_link.user}" ' → => link artist_link.uri, to: artist_link.uri br @@ -75,18 +75,18 @@ - users = Enum.map(@tag.public_links, & &1.user) |> Enum.uniq_by(& &1.id) = for user <- users do - => link user.name, to: Routes.profile_path(@conn, :show, user) + => link user.name, to: ~p"/profiles/#{user}" br = if Enum.any?(@tag.channels) do strong> Associated streams: = for channel <- @tag.channels do - => link channel.title, to: Routes.channel_path(@conn, :show, channel) + => link channel.title, to: ~p"/channels/#{channel}" = if can?(@conn, :edit, channel) do | ( - = link "Edit", to: Routes.channel_path(@conn, :edit, channel) + = link "Edit", to: ~p"/channels/#{channel}/edit" ' ) br @@ -99,7 +99,7 @@ .toggle-box-container .toggle-box-container__content = map_join @tag.implied_by_tags, ", ", fn tag -> - = link tag.name, to: Routes.tag_path(@conn, :show, tag) + = link tag.name, to: ~p"/tags/#{tag}" br @@ -122,5 +122,5 @@ => body | ( - = link "more info", to: Routes.dnp_entry_path(@conn, :show, entry) + = link "more info", to: ~p"/dnp/#{entry}" | ) diff --git a/lib/philomena_web/templates/tag/alias/edit.html.slime b/lib/philomena_web/templates/tag/alias/edit.html.slime index 706545bcc..346aae09a 100644 --- a/lib/philomena_web/templates/tag/alias/edit.html.slime +++ b/lib/philomena_web/templates/tag/alias/edit.html.slime @@ -2,7 +2,7 @@ h1 ' Aliasing tag = @tag.name -= form_for @changeset, Routes.tag_alias_path(@conn, :update, @tag), [method: "put"], fn f -> += form_for @changeset, ~p"/tags/#{@tag}/alias", [method: "put"], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -17,7 +17,7 @@ h1 => submit "Alias tag", class: "button" br -= button_to "Remove tag alias", Routes.tag_alias_path(@conn, :delete, @tag), method: "delete", class: "button", data: [confirm: "Are you really, really sure?", disable_with: raw("Saving…")] += button_to "Remove tag alias", ~p"/tags/#{@tag}/alias", method: "delete", class: "button", data: [confirm: "Are you really, really sure?", disable_with: raw("Saving…")] br -= link "Back", to: Routes.tag_path(@conn, :show, @tag) += link "Back", to: ~p"/tags/#{@tag}" diff --git a/lib/philomena_web/templates/tag/detail/_filters.html.slime b/lib/philomena_web/templates/tag/detail/_filters.html.slime index b09071a87..8dfdb6216 100644 --- a/lib/philomena_web/templates/tag/detail/_filters.html.slime +++ b/lib/philomena_web/templates/tag/detail/_filters.html.slime @@ -9,7 +9,7 @@ table.table tbody = for filter <- @filters do tr - td = link filter.name, to: Routes.filter_path(@conn, :show, filter) + td = link filter.name, to: ~p"/filters/#{filter}" td = cond do - filter.system -> @@ -23,7 +23,7 @@ table.table td = if filter.user do - = link filter.user.name, to: Routes.profile_path(@conn, :show, filter.user) + = link filter.user.name, to: ~p"/profiles/#{filter.user}" - else ' No user associated diff --git a/lib/philomena_web/templates/tag/detail/index.html.slime b/lib/philomena_web/templates/tag/detail/index.html.slime index 8a11358ab..5271cab28 100644 --- a/lib/philomena_web/templates/tag/detail/index.html.slime +++ b/lib/philomena_web/templates/tag/detail/index.html.slime @@ -1,6 +1,6 @@ h1 ' Tag Usage for - = link @tag.name, to: Routes.tag_path(@conn, :show, @tag) + = link @tag.name, to: ~p"/tags/#{@tag}" h3 Filters that spoiler this tag: = render PhilomenaWeb.Tag.DetailView, "_filters.html", filters: @filters_spoilering, conn: @conn @@ -14,4 +14,4 @@ h3 | ) = for u <- @users_watching do - = link u.name, to: Routes.profile_path(@conn, :show, u), class: "interaction-user-list-item" + = link u.name, to: ~p"/profiles/#{u}", class: "interaction-user-list-item" diff --git a/lib/philomena_web/templates/tag/edit.html.slime b/lib/philomena_web/templates/tag/edit.html.slime index 5925112bc..1089bafbd 100644 --- a/lib/philomena_web/templates/tag/edit.html.slime +++ b/lib/philomena_web/templates/tag/edit.html.slime @@ -1,10 +1,10 @@ h1 Editing Tag -p = link "Edit image", to: Routes.tag_image_path(@conn, :edit, @tag) +p = link "Edit image", to: ~p"/tags/#{@tag}/image/edit" = if can?(@conn, :alias, @tag) do - p = link "Edit aliases", to: Routes.tag_alias_path(@conn, :edit, @tag) + p = link "Edit aliases", to: ~p"/tags/#{@tag}/alias/edit" -= form_for @changeset, Routes.tag_path(@conn, :update, @tag), [class: "form"], fn f -> += form_for @changeset, ~p"/tags/#{@tag}", [class: "form"], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -44,10 +44,10 @@ br label for="tag-management" Tag Processing .toggle-box-container .toggle-box-container__content - = button_to "Rebuild index", Routes.tag_reindex_path(@conn, :create, @tag), method: "post", class: "button", data: [confirm: "Are you really, really sure?", disable_with: raw("Reindexing…")] + = button_to "Rebuild index", ~p"/tags/#{@tag}/reindex", method: "post", class: "button", data: [confirm: "Are you really, really sure?", disable_with: raw("Reindexing…")] p Use this if the tag displays the wrong number of images or returns the wrong search results. - = button_to "Destroy tag", Routes.tag_path(@conn, :delete, @tag), method: "delete", class: "button button--danger", data: [confirm: "Are you really, really sure?", disable_with: raw("Deleting…")] + = button_to "Destroy tag", ~p"/tags/#{@tag}", method: "delete", class: "button button--danger", data: [confirm: "Are you really, really sure?", disable_with: raw("Deleting…")] p strong Irreversible. Use with extreme caution! ul diff --git a/lib/philomena_web/templates/tag/image/edit.html.slime b/lib/philomena_web/templates/tag/image/edit.html.slime index e7f538e4e..4965a0dd7 100644 --- a/lib/philomena_web/templates/tag/image/edit.html.slime +++ b/lib/philomena_web/templates/tag/image/edit.html.slime @@ -15,7 +15,7 @@ p Add a new image or remove the existing one here. p SVG is preferred. - = form_for @changeset, Routes.tag_image_path(@conn, :update, @tag), [method: "put", multipart: true], fn f -> + = form_for @changeset, ~p"/tags/#{@tag}/image", [method: "put", multipart: true], fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. @@ -35,7 +35,7 @@ => submit "Update tag image", class: "button" br - = button_to "Remove tag image", Routes.tag_image_path(@conn, :delete, @tag), method: "delete", class: "button", data: [confirm: "Are you really, really sure?"] + = button_to "Remove tag image", ~p"/tags/#{@tag}/image", method: "delete", class: "button", data: [confirm: "Are you really, really sure?"] br - = link "Back", to: Routes.tag_path(@conn, :show, @tag) + = link "Back", to: ~p"/tags/#{@tag}" diff --git a/lib/philomena_web/templates/tag/index.html.slime b/lib/philomena_web/templates/tag/index.html.slime index d19706022..9198e64ba 100644 --- a/lib/philomena_web/templates/tag/index.html.slime +++ b/lib/philomena_web/templates/tag/index.html.slime @@ -1,6 +1,6 @@ h1 Tags -= form_for :tags, Routes.tag_path(@conn, :index), [method: "get", class: "hform", enforce_utf8: false], fn f -> += form_for :tags, ~p"/tags", [method: "get", class: "hform", enforce_utf8: false], fn f -> .field = text_input f, :tq, name: :tq, value: @conn.params["tq"] || "*", class: "input hform__text", placeholder: "Search tags", autocapitalize: "none" = submit "Search", class: "hform__button button" @@ -15,7 +15,7 @@ h2 Search Results = cond do - Enum.any?(@tags) -> - - route = fn p -> Routes.tag_path(@conn, :index, p) end + - route = fn p -> ~p"/tags?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @tags, route: route, params: [tq: @conn.params["tq"] || "*"] = render PhilomenaWeb.TagView, "_tag_list.html", tags: @tags, conn: @conn @@ -56,102 +56,102 @@ table.table td Literal td Matches the name of the target tag, if this tag is aliased. td - code = link "alias_of:twilight sparkle", to: Routes.tag_path(@conn, :index, tq: "alias_of:twilight sparkle") + code = link "alias_of:twilight sparkle", to: ~p"/tags?#{[tq: "alias_of:twilight sparkle"]}" tr td code aliased td Boolean td Matches when this tag is aliased. td - code = link "aliased:true", to: Routes.tag_path(@conn, :index, tq: "aliased:true") + code = link "aliased:true", to: ~p"/tags?#{[tq: "aliased:true"]}" tr td code aliases td Literal td Matches the name of any of this tag's aliases. td - code = link "aliases:ts", to: Routes.tag_path(@conn, :index, tq: "aliases:ts") + code = link "aliases:ts", to: ~p"/tags?#{[tq: "aliases:ts"]}" tr td code analyzed_name td Full Text td Matches the name of this tag. This is the default field. td - code = link "analyzed_name:wing", to: Routes.tag_path(@conn, :index, tq: "analyzed_name:wing") + code = link "analyzed_name:wing", to: ~p"/tags?#{[tq: "analyzed_name:wing"]}" tr td code category td Literal td Matches the category this tag belongs to. td - code = link "category:origin", to: Routes.tag_path(@conn, :index, tq: "category:origin") + code = link "category:origin", to: ~p"/tags?#{[tq: "category:origin"]}" tr td code description td Full Text td Matches the text of the full description for this tag. td - code = link "description:species", to: Routes.tag_path(@conn, :index, tq: "description:species") + code = link "description:species", to: ~p"/tags?#{[tq: "description:species"]}" tr td code id td Numeric Range td Matches the numeric surrogate key for this tag. td - code = link "id:40482", to: Routes.tag_path(@conn, :index, tq: "id:40482") + code = link "id:40482", to: ~p"/tags?#{[tq: "id:40482"]}" tr td code images td Numeric Range td Matches tags with the specified image count. td - code = link "images.lte:1000", to: Routes.tag_path(@conn, :index, tq: "images.lte:1000") + code = link "images.lte:1000", to: ~p"/tags?#{[tq: "images.lte:1000"]}" tr td code implied_by td Literal td Matches this tag if it is implied by the given tag. td - code = link "implied_by:transparent background", to: Routes.tag_path(@conn, :index, tq: "implied_by:transparent background") + code = link "implied_by:transparent background", to: ~p"/tags?#{[tq: "implied_by:transparent background"]}" tr td code implies td Literal td Matches this tag if it implies the given tag. td - code = link "implies:shipping", to: Routes.tag_path(@conn, :index, tq: "implies:shipping") + code = link "implies:shipping", to: ~p"/tags?#{[tq: "implies:shipping"]}" tr td code name td Literal td Matches the exact name of this tag. td - code = link "name:safe", to: Routes.tag_path(@conn, :index, tq: "name:safe") + code = link "name:safe", to: ~p"/tags?#{[tq: "name:safe"]}" tr td code name_in_namespace td Literal td Matches the name of this tag with any namespace component removed. td - code = link "name_in_namespace:johnjoseco", to: Routes.tag_path(@conn, :index, tq: "name_in_namespace:johnjoseco") + code = link "name_in_namespace:johnjoseco", to: ~p"/tags?#{[tq: "name_in_namespace:johnjoseco"]}" tr td code namespace td Literal td Matches tags with the given namespace. td - code = link "namespace:artist", to: Routes.tag_path(@conn, :index, tq: "namespace:artist") + code = link "namespace:artist", to: ~p"/tags?#{[tq: "namespace:artist"]}" tr td code short_description td Full Text td Matches the text of the short description for this tag. td - code = link "short_description:gender", to: Routes.tag_path(@conn, :index, tq: "short_description:gender") + code = link "short_description:gender", to: ~p"/tags?#{[tq: "short_description:gender"]}" tr td code slug td Literal td Matches the slug of this tag. td - code = link "slug:-fwslash-mlp-fwslash-", to: Routes.tag_path(@conn, :index, tq: "slug:-fwslash-mlp-fwslash-") + code = link "slug:-fwslash-mlp-fwslash-", to: ~p"/tags?#{[tq: "slug:-fwslash-mlp-fwslash-"]}" diff --git a/lib/philomena_web/templates/tag/show.html.slime b/lib/philomena_web/templates/tag/show.html.slime index a8b85c431..ad85fbed5 100644 --- a/lib/philomena_web/templates/tag/show.html.slime +++ b/lib/philomena_web/templates/tag/show.html.slime @@ -1,3 +1,3 @@ -= render PhilomenaWeb.ImageView, "index.html", conn: @conn, tags: @tags, images: @images, header: "Images tagged #{@tag.name}", scope: scope(@conn), route: fn p -> Routes.tag_path(@conn, :show, @tag, p) end += render PhilomenaWeb.ImageView, "index.html", conn: @conn, tags: @tags, images: @images, header: "Images tagged #{@tag.name}", scope: scope(@conn), route: fn p -> ~p"/tags/#{@tag}?#{p}" end = render PhilomenaWeb.SearchView, "_form.html", conn: @conn diff --git a/lib/philomena_web/templates/tag/tag_change/index.html.slime b/lib/philomena_web/templates/tag/tag_change/index.html.slime index 5c9aed293..b4ce6a576 100644 --- a/lib/philomena_web/templates/tag/tag_change/index.html.slime +++ b/lib/philomena_web/templates/tag/tag_change/index.html.slime @@ -1,9 +1,9 @@ h1 ' Tag changes on tag - a href=Routes.tag_path(@conn, :show, @tag) + a href=~p"/tags/#{@tag}" = @tag.name -- route = fn p -> Routes.tag_tag_change_path(@conn, :index, @tag, p) end +- route = fn p -> ~p"/tags/#{@tag}/tag_changes?#{p}" end - params = if @conn.params["added"], do: [added: @conn.params["added"]] - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @tag_changes, route: route, conn: @conn, params: params @@ -12,8 +12,8 @@ h1 span | Display only: - = link "Removed", to: Routes.tag_tag_change_path(@conn, :index, @tag, added: 0) - = link "Added", to: Routes.tag_tag_change_path(@conn, :index, @tag, added: 1) - = link "All", to: Routes.tag_tag_change_path(@conn, :index, @tag) + = link "Removed", to: ~p"/tags/#{@tag}/tag_changes?#{[added: 0]}" + = link "Added", to: ~p"/tags/#{@tag}/tag_changes?#{[added: 1]}" + = link "All", to: ~p"/tags/#{@tag}/tag_changes" = render PhilomenaWeb.TagChangeView, "index.html", conn: @conn, tag_changes: @tag_changes, pagination: pagination diff --git a/lib/philomena_web/templates/tag_change/index.html.slime b/lib/philomena_web/templates/tag_change/index.html.slime index a5f6b64bc..cfffd345c 100644 --- a/lib/philomena_web/templates/tag_change/index.html.slime +++ b/lib/philomena_web/templates/tag_change/index.html.slime @@ -7,7 +7,7 @@ i.fa.fa-check> ' Toggle all -= form_for :tag_changes, Routes.tag_change_revert_path(@conn, :create), [class: "tag-changes-form"], fn _f -> += form_for :tag_changes, ~p"/tag_changes/revert", [class: "tag-changes-form"], fn _f -> .block__content table.table thead @@ -31,7 +31,7 @@ input type="checkbox" name="ids[]" value=tag_change.id td.center - = link tag_change.image_id, to: Routes.image_path(@conn, :show, tag_change.image) + = link tag_change.image_id, to: ~p"/images/#{tag_change.image}" td.center = render PhilomenaWeb.ImageView, "_image_container.html", image: tag_change.image, size: :thumb_tiny, conn: @conn @@ -69,7 +69,7 @@ td.danger No = if reverts_tag_changes?(@conn) do td - a href=Routes.image_tag_change_path(@conn, :delete, tag_change.image, tag_change) data-method="delete" data-confirm="Are you really, really sure?" + a href=~p"/images/#{tag_change.image}/tag_changes/#{tag_change}" data-method="delete" data-confirm="Are you really, really sure?" ' Delete from history .block__header diff --git a/lib/philomena_web/templates/topic/new.html.slime b/lib/philomena_web/templates/topic/new.html.slime index 5d42d130e..05236f7aa 100644 --- a/lib/philomena_web/templates/topic/new.html.slime +++ b/lib/philomena_web/templates/topic/new.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.forum_topic_path(@conn, :create, @forum), fn f -> += form_for @changeset, ~p"/forums/#{@forum}/topics", fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/topic/poll/_display.html.slime b/lib/philomena_web/templates/topic/poll/_display.html.slime index ed185facb..37e7711c9 100644 --- a/lib/philomena_web/templates/topic/poll/_display.html.slime +++ b/lib/philomena_web/templates/topic/poll/_display.html.slime @@ -2,7 +2,7 @@ = link "Voting", to: "#", class: "selected", data: [click_tab: "voting"] = if can?(@conn, :hide, @topic) do - = link "Voters", to: "#", data: [click_tab: "voters", load_tab: Routes.forum_topic_poll_vote_path(@conn, :index, @forum, @topic)] + = link "Voters", to: "#", data: [click_tab: "voters", load_tab: ~p"/forums/#{@forum}/topics/#{@topic}/poll/votes"] = link "Administrate", to: "#", data: [click_tab: "administration"] .block__tab data-tab="voting" @@ -31,6 +31,6 @@ .block__tab.hidden data-tab="voters" p Loading… .block__tab.hidden data-tab="administration" - a.button.button--warning.js-staff-action> href=Routes.forum_topic_poll_path(@conn, :edit, @forum, @topic) + a.button.button--warning.js-staff-action> href=~p"/forums/#{@forum}/topics/#{@topic}/poll/edit" i.fa.fa-edit> | Edit diff --git a/lib/philomena_web/templates/topic/poll/_vote_form.html.slime b/lib/philomena_web/templates/topic/poll/_vote_form.html.slime index 080833bc6..7368acdfd 100644 --- a/lib/philomena_web/templates/topic/poll/_vote_form.html.slime +++ b/lib/philomena_web/templates/topic/poll/_vote_form.html.slime @@ -1,4 +1,4 @@ -= form_for :poll_vote, Routes.forum_topic_poll_vote_path(@conn, :create, @forum, @topic), [class: "poll-vote-form"], fn _f -> += form_for :poll_vote, ~p"/forums/#{@forum}/topics/#{@topic}/poll/votes", [class: "poll-vote-form"], fn _f -> h4.poll__header ' Poll: = @poll.title diff --git a/lib/philomena_web/templates/topic/poll/edit.html.slime b/lib/philomena_web/templates/topic/poll/edit.html.slime index 1282a99eb..03cf0640e 100644 --- a/lib/philomena_web/templates/topic/poll/edit.html.slime +++ b/lib/philomena_web/templates/topic/poll/edit.html.slime @@ -1,6 +1,6 @@ h1 Editing Poll -= form_for @changeset, Routes.forum_topic_poll_path(@conn, :update, @forum, @topic), fn f -> += form_for @changeset, ~p"/forums/#{@forum}/topics/#{@topic}/poll", fn f -> = render PhilomenaWeb.Topic.PollView, "_form.html", f: f br diff --git a/lib/philomena_web/templates/topic/poll/vote/index.html.slime b/lib/philomena_web/templates/topic/poll/vote/index.html.slime index 5b71a1006..749d9cb3f 100644 --- a/lib/philomena_web/templates/topic/poll/vote/index.html.slime +++ b/lib/philomena_web/templates/topic/poll/vote/index.html.slime @@ -4,8 +4,8 @@ = for vote <- option.poll_votes do span.interaction-user-list-item - => link vote.user.name, to: Routes.profile_path(@conn, :show, vote.user) - /= link "(x)", to: Routes.forum_topic_poll_vote_path(@conn, :delete, @forum, @topic, vote) + => link vote.user.name, to: ~p"/profiles/#{vote.user}" + /= link "(x)", to: ~p"/forums/#{@forum}/topics/#{@topic}/poll/votes/#{vote}" - else p diff --git a/lib/philomena_web/templates/topic/post/_form.html.slime b/lib/philomena_web/templates/topic/post/_form.html.slime index aa69b979a..dcb15f40a 100644 --- a/lib/philomena_web/templates/topic/post/_form.html.slime +++ b/lib/philomena_web/templates/topic/post/_form.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.forum_topic_post_path(@conn, :create, @forum, @topic), fn f -> += form_for @changeset, ~p"/forums/#{@forum}/topics/#{@topic}/posts", fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/topic/post/edit.html.slime b/lib/philomena_web/templates/topic/post/edit.html.slime index 5f815ff2d..b75441216 100644 --- a/lib/philomena_web/templates/topic/post/edit.html.slime +++ b/lib/philomena_web/templates/topic/post/edit.html.slime @@ -1,4 +1,4 @@ -= form_for @changeset, Routes.forum_topic_post_path(@conn, :update, @post.topic.forum, @post.topic, @post), fn f -> += form_for @changeset, ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}", fn f -> = if @changeset.action do .alert.alert-danger p Oops, something went wrong! Please check the errors below. diff --git a/lib/philomena_web/templates/topic/post/history/index.html.slime b/lib/philomena_web/templates/topic/post/history/index.html.slime index 59660d72d..d204a40e8 100644 --- a/lib/philomena_web/templates/topic/post/history/index.html.slime +++ b/lib/philomena_web/templates/topic/post/history/index.html.slime @@ -2,7 +2,7 @@ h1 ' Viewing last 25 versions of post by = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @post, conn: @conn ' in topic - a href=(Routes.forum_topic_path(@conn, :show, @post.topic.forum, @post.topic, post_id: @post.id) <> "#post_#{@post.id}") + a href=(~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}") = @post.topic.title = for version <- @versions do diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index cbda4007d..e78b13b43 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -1,4 +1,4 @@ -- route = fn p -> Routes.forum_topic_path(@conn, :show, @forum, @topic, p) end +- route = fn p -> ~p"/forums/#{@forum}/topics/#{@topic}?#{p}" end - pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @posts, route: route, last: true h1 = @topic.title @@ -16,22 +16,22 @@ h1 = @topic.title strong> Deleted by: em strong - = link(@topic.deleted_by.name, to: Routes.profile_path(@conn, :show, @topic.deleted_by)) + = link(@topic.deleted_by.name, to: ~p"/profiles/#{@topic.deleted_by}") p - = link(to: Routes.forum_topic_hide_path(@conn, :delete, @forum, @topic), method: :delete, class: "button") do + = link(to: ~p"/forums/#{@forum}/topics/#{@topic}/hide", method: :delete, class: "button") do i.fas.fa-check> ' Restore / Header section .block .block__header - => link(@forum.name, to: Routes.forum_path(@conn, :show, @forum)) + => link(@forum.name, to: ~p"/forums/#{@forum}") ' » - => link(@topic.title, to: Routes.forum_topic_path(@conn, :show, @forum, @topic)) + => link(@topic.title, to: ~p"/forums/#{@forum}/topics/#{@topic}") = if not @topic.hidden_from_users or can?(@conn, :hide, @topic) do - a href=Routes.post_path(@conn, :index, pq: "topic_id:#{@topic.id}") + a href=~p"/posts?#{[pq: "topic_id:#{@topic.id}"]}" i.fa.icon--padded.small.fa-search> ' Search Posts .flex.flex--wrap.block__header.block__header--light.page__header @@ -86,7 +86,7 @@ h1 = @topic.title ' Locked by: em strong - = link(@topic.locked_by.name, to: Routes.profile_path(@conn, :show, @topic.locked_by)) + = link(@topic.locked_by.name, to: ~p"/profiles/#{@topic.locked_by}") / Post form = cond do @@ -116,43 +116,43 @@ h1 = @topic.title p = if not @topic.hidden_from_users do = if @topic.sticky do - = link(to: Routes.forum_topic_stick_path(@conn, :delete, @forum, @topic), method: :delete, class: "button") do + = link(to: ~p"/forums/#{@forum}/topics/#{@topic}/stick", method: :delete, class: "button") do i.fas.fa-thumbtack> ' Unstick - else - = link(to: Routes.forum_topic_stick_path(@conn, :create, @forum, @topic), method: :post, class: "button") do + = link(to: ~p"/forums/#{@forum}/topics/#{@topic}/stick", method: :post, class: "button") do i.fas.fa-thumbtack> ' Stick = if @topic.locked_at do - = link(to: Routes.forum_topic_lock_path(@conn, :delete, @forum, @topic), method: :delete, class: "button") do + = link(to: ~p"/forums/#{@forum}/topics/#{@topic}/lock", method: :delete, class: "button") do i.fas.fa-unlock> ' Unlock - else - = form_for :topic, Routes.forum_topic_lock_path(@conn, :create, @forum, @topic), [method: :post, class: "hform"], fn f -> + = form_for :topic, ~p"/forums/#{@forum}/topics/#{@topic}/lock", [method: :post, class: "hform"], fn f -> .field = text_input f, :lock_reason, class: "input hform__text", placeholder: "Lock reason", required: true = submit class: "hform__button button" do i.fas.fa-lock> ' Lock - = form_for @topic_changeset, Routes.forum_topic_path(@conn, :update, @forum, @topic), [method: :put, class: "hform"], fn f -> + = form_for @topic_changeset, ~p"/forums/#{@forum}/topics/#{@topic}", [method: :put, class: "hform"], fn f -> .field = text_input f, :title, class: "input hform__text", placeholder: "New Title" = submit class: "hform__button button" do i.fas.fa-pen> ' Set Title - = form_for :topic, Routes.forum_topic_move_path(@conn, :create, @forum, @topic), [method: :post, class: "hform"], fn f -> + = form_for :topic, ~p"/forums/#{@forum}/topics/#{@topic}/move", [method: :post, class: "hform"], fn f -> .field = select f, :target_forum_id, Enum.map(@conn.assigns.forums, &{&1.name, &1.id}), class: "input hform__text" = submit class: "hform__button button" do i.fas.fa-truck> ' Move - = form_for :topic, Routes.forum_topic_hide_path(@conn, :create, @forum, @topic), [method: :post, class: "hform"], fn f -> + = form_for :topic, ~p"/forums/#{@forum}/topics/#{@topic}/hide", [method: :post, class: "hform"], fn f -> .field = text_input f, :deletion_reason, class: "input hform__text", placeholder: "Deletion reason", required: true = submit class: "hform__button button" do diff --git a/lib/philomena_web/templates/topic/subscription/_subscription.html.slime b/lib/philomena_web/templates/topic/subscription/_subscription.html.slime index bda8240f0..144225796 100644 --- a/lib/philomena_web/templates/topic/subscription/_subscription.html.slime +++ b/lib/philomena_web/templates/topic/subscription/_subscription.html.slime @@ -1,8 +1,8 @@ elixir: - watch_path = Routes.forum_topic_subscription_path(@conn, :create, @forum, @topic) + watch_path = ~p"/forums/#{@forum}/topics/#{@topic}/subscription" watch_class = if @watching, do: "hidden", else: "" - unwatch_path = Routes.forum_topic_subscription_path(@conn, :delete, @forum, @topic) + unwatch_path = ~p"/forums/#{@forum}/topics/#{@topic}/subscription" unwatch_class = if @watching, do: "", else: "hidden" = if @conn.assigns.current_user do @@ -17,7 +17,7 @@ elixir: span.hidden--mobile ' Unsubscribe - else - a href=Routes.session_path(@conn, :new) + a href=~p"/sessions/new" i.fa.fa-bell> span.hidden--mobile ' Subscribe diff --git a/lib/philomena_web/templates/unlock/new.html.slime b/lib/philomena_web/templates/unlock/new.html.slime index 5d64af56a..9c8e05cca 100644 --- a/lib/philomena_web/templates/unlock/new.html.slime +++ b/lib/philomena_web/templates/unlock/new.html.slime @@ -1,6 +1,6 @@ h1 Resend unlock instructions -= form_for :user, Routes.unlock_path(@conn, :create), fn f -> += form_for :user, ~p"/unlocks", fn f -> .field = email_input f, :email, placeholder: "Email", class: "input", required: true diff --git a/lib/philomena_web/templates/user_attribution/_anon_user.html.slime b/lib/philomena_web/templates/user_attribution/_anon_user.html.slime index f58c6cde5..2f13abb70 100644 --- a/lib/philomena_web/templates/user_attribution/_anon_user.html.slime +++ b/lib/philomena_web/templates/user_attribution/_anon_user.html.slime @@ -4,13 +4,13 @@ - icon = user_icon(@object.user) = if icon do i class="fa #{icon}" - = link(@object.user.name, to: Routes.profile_path(@conn, :show, @object.user)) + = link(@object.user.name, to: ~p"/profiles/#{@object.user}") = if assigns[:awards] do = render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards - not is_nil(@object.user) and (can?(@conn, :reveal_anon, @object) and not hide_staff_tools?(@conn)) -> strong<> - = link(anonymous_name(@object, true), to: Routes.profile_path(@conn, :show, @object.user)) + = link(anonymous_name(@object, true), to: ~p"/profiles/#{@object.user}") - true -> strong<> diff --git a/lib/philomena_web/templates/user_attribution/_user.html.slime b/lib/philomena_web/templates/user_attribution/_user.html.slime index 63013ac1e..848706b41 100644 --- a/lib/philomena_web/templates/user_attribution/_user.html.slime +++ b/lib/philomena_web/templates/user_attribution/_user.html.slime @@ -3,6 +3,6 @@ - icon = user_icon(@object.user) = if icon do i class="fa #{icon}"> - = link(@object.user.name, to: Routes.profile_path(@conn, :show, @object.user)) + = link(@object.user.name, to: ~p"/profiles/#{@object.user}") = if assigns[:awards] do = render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards diff --git a/lib/philomena_web/user_auth.ex b/lib/philomena_web/user_auth.ex index 9d00e768a..b815537f8 100644 --- a/lib/philomena_web/user_auth.ex +++ b/lib/philomena_web/user_auth.ex @@ -3,7 +3,6 @@ defmodule PhilomenaWeb.UserAuth do import Phoenix.Controller alias Philomena.Users - alias PhilomenaWeb.Router.Helpers, as: Routes alias PhilomenaWeb.UserIpUpdater alias PhilomenaWeb.UserFingerprintUpdater @@ -196,7 +195,7 @@ defmodule PhilomenaWeb.UserAuth do conn |> put_flash(:warning, "You must log in to access this page.") |> maybe_store_return_to() - |> redirect(to: Routes.session_path(conn, :new)) + |> redirect(to: ~p"/sessions/new") |> halt() end end diff --git a/lib/philomena_web/views/admin/approval_view.ex b/lib/philomena_web/views/admin/approval_view.ex index 03b0f3c2c..b90fa752d 100644 --- a/lib/philomena_web/views/admin/approval_view.ex +++ b/lib/philomena_web/views/admin/approval_view.ex @@ -4,7 +4,7 @@ defmodule PhilomenaWeb.Admin.ApprovalView do alias PhilomenaWeb.Admin.ReportView # Shamelessly copied from ReportView - def truncated_ip_link(conn, ip), do: ReportView.truncated_ip_link(conn, ip) + def truncated_ip_link(ip), do: ReportView.truncated_ip_link(ip) def image_thumb(conn, image) do render(PhilomenaWeb.ImageView, "_image_container.html", diff --git a/lib/philomena_web/views/admin/ban_view.ex b/lib/philomena_web/views/admin/ban_view.ex index 0df1e7305..59fbe1de1 100644 --- a/lib/philomena_web/views/admin/ban_view.ex +++ b/lib/philomena_web/views/admin/ban_view.ex @@ -1,8 +1,8 @@ defmodule PhilomenaWeb.Admin.BanView do alias PhilomenaWeb.ProfileView - def user_abbrv(conn, user), - do: ProfileView.user_abbrv(conn, user) + def user_abbrv(user), + do: ProfileView.user_abbrv(user) def ban_row_class(%{valid_until: until, enabled: enabled}) do now = DateTime.utc_now() diff --git a/lib/philomena_web/views/admin/mod_note_view.ex b/lib/philomena_web/views/admin/mod_note_view.ex index a873208aa..021e47211 100644 --- a/lib/philomena_web/views/admin/mod_note_view.ex +++ b/lib/philomena_web/views/admin/mod_note_view.ex @@ -5,20 +5,20 @@ defmodule PhilomenaWeb.Admin.ModNoteView do alias Philomena.Reports.Report alias Philomena.DnpEntries.DnpEntry - def link_to_noted_thing(conn, %DnpEntry{tag: tag} = dnp_entry), - do: link("DNP entry for #{tag.name}", to: Routes.dnp_entry_path(conn, :show, dnp_entry)) + def link_to_noted_thing(%DnpEntry{tag: tag} = dnp_entry), + do: link("DNP entry for #{tag.name}", to: ~p"/dnp/#{dnp_entry}") - def link_to_noted_thing(conn, %Report{user: nil} = report), - do: link("Report #{report.id}", to: Routes.admin_report_path(conn, :show, report)) + def link_to_noted_thing(%Report{user: nil} = report), + do: link("Report #{report.id}", to: ~p"/admin/reports/#{report}") - def link_to_noted_thing(conn, %Report{user: user} = report), + def link_to_noted_thing(%Report{user: user} = report), do: link("Report #{report.id} by #{user.name}", - to: Routes.admin_report_path(conn, :show, report) + to: ~p"/admin/reports/#{report}" ) - def link_to_noted_thing(conn, %User{} = user), - do: link("User #{user.name}", to: Routes.profile_path(conn, :show, user)) + def link_to_noted_thing(%User{} = user), + do: link("User #{user.name}", to: ~p"/profiles/#{user}") - def link_to_noted_thing(_conn, _notable), do: "Item permanently deleted" + def link_to_noted_thing(_notable), do: "Item permanently deleted" end diff --git a/lib/philomena_web/views/admin/report_view.ex b/lib/philomena_web/views/admin/report_view.ex index a9cd5bcc2..82de807dd 100644 --- a/lib/philomena_web/views/admin/report_view.ex +++ b/lib/philomena_web/views/admin/report_view.ex @@ -7,8 +7,8 @@ defmodule PhilomenaWeb.Admin.ReportView do alias PhilomenaWeb.ReportView alias PhilomenaWeb.ProfileView - defp link_to_reported_thing(conn, reportable), - do: ReportView.link_to_reported_thing(conn, reportable) + defp link_to_reported_thing(reportable), + do: ReportView.link_to_reported_thing(reportable) defp report_row_class(report), do: ReportView.report_row_class(report) @@ -16,8 +16,8 @@ defmodule PhilomenaWeb.Admin.ReportView do defp pretty_state(report), do: ReportView.pretty_state(report) - defp user_abbrv(conn, user), - do: ProfileView.user_abbrv(conn, user) + defp user_abbrv(user), + do: ProfileView.user_abbrv(user) defp current?(current_user, user), do: ProfileView.current?(current_user, user) @@ -25,13 +25,13 @@ defmodule PhilomenaWeb.Admin.ReportView do def truncate(<>), do: string <> "..." def truncate(string), do: string - def truncated_ip_link(conn, ip) do + def truncated_ip_link(ip) do case to_string(ip) do <> = ip -> - link(string <> "...", to: Routes.ip_profile_path(conn, :show, ip)) + link(string <> "...", to: ~p"/ip_profiles/#{ip}") ip -> - link(ip, to: Routes.ip_profile_path(conn, :show, ip)) + link(ip, to: ~p"/ip_profiles/#{ip}") end end diff --git a/lib/philomena_web/views/conversation_view.ex b/lib/philomena_web/views/conversation_view.ex index 4ce912831..97ce9c999 100644 --- a/lib/philomena_web/views/conversation_view.ex +++ b/lib/philomena_web/views/conversation_view.ex @@ -32,9 +32,9 @@ defmodule PhilomenaWeb.ConversationView do end end - def last_message_path(conn, conversation, count) do + def last_message_path(conversation, count) do page = trunc(Float.ceil(count / 25)) - Routes.conversation_path(conn, :show, conversation, page: page) + ~p"/conversations/#{conversation}?#{[page: page]}" end end diff --git a/lib/philomena_web/views/profile_view.ex b/lib/philomena_web/views/profile_view.ex index e1e73f4de..a10b9e8b4 100644 --- a/lib/philomena_web/views/profile_view.ex +++ b/lib/philomena_web/views/profile_view.ex @@ -54,13 +54,20 @@ defmodule PhilomenaWeb.ProfileView do sy = height / 20 factor = 100 / 90 - content_tag :svg, id: "js-graph-svg", width: "100%", preserveAspectRatio: "xMinYMin", viewBox: "0 0 #{width} #{height}" do + content_tag :svg, + id: "js-graph-svg", + width: "100%", + preserveAspectRatio: "xMinYMin", + viewBox: "0 0 #{width} #{height}" do first = List.first(data) last = List.last(data) first_y = sparkline_y(first, max) * sy last_y = sparkline_y(last, max) * sy - indexed_data = data + + indexed_data = + data |> Enum.with_index() + points = indexed_data |> Enum.chunk_every(2, 1, :discard) @@ -68,19 +75,30 @@ defmodule PhilomenaWeb.ProfileView do cy = sparkline_y(cv, max) ny = sparkline_y(nv, max) - "C #{(ci * sx) + 0.5 * sx},#{cy * sy} #{(ni * sx) - 0.5 * sx},#{ny * sy} #{ni * sx},#{ny * sy}" + "C #{ci * sx + 0.5 * sx},#{cy * sy} #{ni * sx - 0.5 * sx},#{ny * sy} #{ni * sx},#{ny * sy}" end) |> Enum.join("") - circles = for {val, i} <- indexed_data do - y = sparkline_y(val, max) * sy - - content_tag :circle, class: "barline__dot", cx: "#{i * factor}%", cy: y * sy + 1.25, r: 2.5 do - content_tag(:title, val) + circles = + for {val, i} <- indexed_data do + y = sparkline_y(val, max) * sy + + content_tag :circle, + class: "barline__dot", + cx: "#{i * factor}%", + cy: y * sy + 1.25, + r: 2.5 do + content_tag(:title, val) + end end - end - graph = content_tag :path, "", id: "js-graph", class: "barline__bar", d: "M0,#{first_y}#{points}L#{width - sx},#{last_y}L#{width - sx},#{height}L0,#{height}L0,#{first_y}" + graph = + content_tag(:path, "", + id: "js-graph", + class: "barline__bar", + d: + "M0,#{first_y}#{points}L#{width - sx},#{last_y}L#{width - sx},#{height}L0,#{height}L0,#{first_y}" + ) [graph, circles] end @@ -105,16 +123,16 @@ defmodule PhilomenaWeb.ProfileView do def enabled_text(true), do: "Enabled" def enabled_text(_else), do: "Disabled" - def user_abbrv(conn, %{name: name} = user) do + def user_abbrv(%{name: name} = user) do abbrv = String.upcase(initials_abbrv(name) || uppercase_abbrv(name) || first_letters_abbrv(name)) abbrv = "(" <> abbrv <> ")" - link(abbrv, to: Routes.profile_path(conn, :show, user)) + link(abbrv, to: ~p"/profiles/#{user}") end - def user_abbrv(_conn, _user), do: content_tag(:span, "(n/a)") + def user_abbrv(_user), do: content_tag(:span, "(n/a)") defp initials_abbrv(name) do case String.split(name, " ", parts: 4) do diff --git a/lib/philomena_web/views/report_view.ex b/lib/philomena_web/views/report_view.ex index 7ffae6937..f65fcf098 100644 --- a/lib/philomena_web/views/report_view.ex +++ b/lib/philomena_web/views/report_view.ex @@ -41,42 +41,42 @@ defmodule PhilomenaWeb.ReportView do def pretty_state(%{state: "claimed"}), do: "Claimed" def pretty_state(_report), do: "Open" - def link_to_reported_thing(conn, %Image{} = r), - do: link("Image >>#{r.id}", to: Routes.image_path(conn, :show, r)) + def link_to_reported_thing(%Image{} = r), + do: link("Image >>#{r.id}", to: ~p"/images/#{r}") - def link_to_reported_thing(conn, %Comment{} = r), + def link_to_reported_thing(%Comment{} = r), do: link("Comment on image >>#{r.image.id}", - to: Routes.image_path(conn, :show, r.image) <> "#comment_#{r.id}" + to: ~p"/images/#{r.image}" <> "#comment_#{r.id}" ) - def link_to_reported_thing(conn, %Conversation{} = r), + def link_to_reported_thing(%Conversation{} = r), do: link("Conversation between #{r.from.name} and #{r.to.name}", - to: Routes.conversation_path(conn, :show, r) + to: ~p"/conversations/#{r}" ) - def link_to_reported_thing(conn, %Commission{} = r), + def link_to_reported_thing(%Commission{} = r), do: link("#{r.user.name}'s commission page", - to: Routes.profile_commission_path(conn, :show, r.user) + to: ~p"/profiles/#{r.user}/commission" ) - def link_to_reported_thing(conn, %Gallery{} = r), - do: link("Gallery '#{r.title}' by #{r.creator.name}", to: Routes.gallery_path(conn, :show, r)) + def link_to_reported_thing(%Gallery{} = r), + do: link("Gallery '#{r.title}' by #{r.creator.name}", to: ~p"/galleries/#{r}") - def link_to_reported_thing(conn, %Post{} = r), + def link_to_reported_thing(%Post{} = r), do: link("Post in #{r.topic.title}", to: - Routes.forum_topic_path(conn, :show, r.topic.forum, r.topic, post_id: r.id) <> + ~p"/forums/#{r.topic.forum}/topics/#{r.topic}?#{[post_id: r.id]}" <> "#post_#{r.id}" ) - def link_to_reported_thing(conn, %User{} = r), - do: link("User '#{r.name}'", to: Routes.profile_path(conn, :show, r)) + def link_to_reported_thing(%User{} = r), + do: link("User '#{r.name}'", to: ~p"/profiles/#{r}") - def link_to_reported_thing(_conn, _reportable) do + def link_to_reported_thing(_reportable) do "Reported item permanently destroyed." end end diff --git a/lib/philomena_web/views/setting_view.ex b/lib/philomena_web/views/setting_view.ex index ea95c8164..a9eb68cd2 100644 --- a/lib/philomena_web/views/setting_view.ex +++ b/lib/philomena_web/views/setting_view.ex @@ -51,7 +51,7 @@ defmodule PhilomenaWeb.SettingView do ] end - def light_theme_options(conn) do + def light_theme_options do [ [ key: "Red", diff --git a/test/philomena_web/controllers/confirmation_controller_test.exs b/test/philomena_web/controllers/confirmation_controller_test.exs index 55cbb1726..049fd6d1f 100644 --- a/test/philomena_web/controllers/confirmation_controller_test.exs +++ b/test/philomena_web/controllers/confirmation_controller_test.exs @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do describe "GET /confirmations/new" do test "renders the confirmation page", %{conn: conn} do - conn = get(conn, Routes.confirmation_path(conn, :new)) + conn = get(conn, ~p"/confirmations/new") response = html_response(conn, 200) assert response =~ "

Resend confirmation instructions

" end @@ -22,7 +22,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do @tag :capture_log test "sends a new confirmation token", %{conn: conn, user: user} do conn = - post(conn, Routes.confirmation_path(conn, :create), %{ + post(conn, ~p"/confirmations", %{ "user" => %{"email" => user.email} }) @@ -35,7 +35,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do Repo.update!(Users.User.confirm_changeset(user)) conn = - post(conn, Routes.confirmation_path(conn, :create), %{ + post(conn, ~p"/confirmations", %{ "user" => %{"email" => user.email} }) @@ -46,7 +46,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do test "does not send confirmation token if email is invalid", %{conn: conn} do conn = - post(conn, Routes.confirmation_path(conn, :create), %{ + post(conn, ~p"/confirmations", %{ "user" => %{"email" => "unknown@example.com"} }) @@ -63,14 +63,14 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do Users.deliver_user_confirmation_instructions(user, url) end) - conn = get(conn, Routes.confirmation_path(conn, :show, token)) + conn = get(conn, ~p"/confirmations/#{token}") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :info) =~ "Account confirmed successfully" assert Users.get_user!(user.id).confirmed_at refute get_session(conn, :user_token) assert Repo.all(Users.UserToken) == [] - conn = get(conn, Routes.confirmation_path(conn, :show, token)) + conn = get(conn, ~p"/confirmations/#{token}") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :error) =~ @@ -78,7 +78,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do end test "does not confirm email with invalid token", %{conn: conn, user: user} do - conn = get(conn, Routes.confirmation_path(conn, :show, "oops")) + conn = get(conn, ~p"/confirmations/oops") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :error) =~ diff --git a/test/philomena_web/controllers/password_controller_test.exs b/test/philomena_web/controllers/password_controller_test.exs index 4d421f723..b74cf0462 100644 --- a/test/philomena_web/controllers/password_controller_test.exs +++ b/test/philomena_web/controllers/password_controller_test.exs @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do describe "GET /passwords/new" do test "renders the reset password page", %{conn: conn} do - conn = get(conn, Routes.password_path(conn, :new)) + conn = get(conn, ~p"/passwords/new") html_response(conn, 200) end end @@ -21,7 +21,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do @tag :capture_log test "sends a new reset password token", %{conn: conn, user: user} do conn = - post(conn, Routes.password_path(conn, :create), %{ + post(conn, ~p"/passwords", %{ "user" => %{"email" => user.email} }) @@ -32,7 +32,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do test "does not send reset password token if email is invalid", %{conn: conn} do conn = - post(conn, Routes.password_path(conn, :create), %{ + post(conn, ~p"/passwords", %{ "user" => %{"email" => "unknown@example.com"} }) @@ -53,12 +53,12 @@ defmodule PhilomenaWeb.PasswordControllerTest do end test "renders reset password", %{conn: conn, token: token} do - conn = get(conn, Routes.password_path(conn, :edit, token)) + conn = get(conn, ~p"/passwords/#{token}/edit") html_response(conn, 200) end test "does not render reset password with invalid token", %{conn: conn} do - conn = get(conn, Routes.password_path(conn, :edit, "oops")) + conn = get(conn, ~p"/passwords/oops/edit") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :error) =~ @@ -78,14 +78,14 @@ defmodule PhilomenaWeb.PasswordControllerTest do test "resets password once", %{conn: conn, user: user, token: token} do conn = - put(conn, Routes.password_path(conn, :update, token), %{ + put(conn, ~p"/passwords/#{token}", %{ "user" => %{ "password" => "new valid password", "password_confirmation" => "new valid password" } }) - assert redirected_to(conn) == Routes.session_path(conn, :new) + assert redirected_to(conn) == ~p"/sessions/new" refute get_session(conn, :user_token) assert Flash.get(conn.assigns.flash, :info) =~ "Password reset successfully" assert Users.get_user_by_email_and_password(user.email, "new valid password", & &1) @@ -93,7 +93,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do test "does not reset password on invalid data", %{conn: conn, token: token} do conn = - put(conn, Routes.password_path(conn, :update, token), %{ + put(conn, ~p"/passwords/#{token}", %{ "user" => %{ "password" => "too short", "password_confirmation" => "does not match" @@ -106,7 +106,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do end test "does not reset password with invalid token", %{conn: conn} do - conn = put(conn, Routes.password_path(conn, :update, "oops")) + conn = put(conn, ~p"/passwords/oops") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :error) =~ diff --git a/test/philomena_web/controllers/registration/email_controller_test.exs b/test/philomena_web/controllers/registration/email_controller_test.exs index 2107a80f0..756fea2e4 100644 --- a/test/philomena_web/controllers/registration/email_controller_test.exs +++ b/test/philomena_web/controllers/registration/email_controller_test.exs @@ -11,19 +11,19 @@ defmodule PhilomenaWeb.Registration.EmailControllerTest do @tag :capture_log test "updates the user email", %{conn: conn, user: user} do conn = - post(conn, Routes.registration_email_path(conn, :create), %{ + post(conn, ~p"/registrations/email", %{ "current_password" => valid_user_password(), "user" => %{"email" => unique_user_email()} }) - assert redirected_to(conn) == Routes.registration_path(conn, :edit) + assert redirected_to(conn) == ~p"/registrations/edit" assert Flash.get(conn.assigns.flash, :info) =~ "A link to confirm your email" assert Users.get_user_by_email(user.email) end test "does not update email on invalid data", %{conn: conn} do conn = - post(conn, Routes.registration_email_path(conn, :create), %{ + post(conn, ~p"/registrations/email", %{ "current_password" => "invalid", "user" => %{"email" => "with spaces"} }) @@ -45,22 +45,22 @@ defmodule PhilomenaWeb.Registration.EmailControllerTest do end test "updates the user email once", %{conn: conn, user: user, token: token, email: email} do - conn = get(conn, Routes.registration_email_path(conn, :show, token)) - assert redirected_to(conn) == Routes.registration_path(conn, :edit) + conn = get(conn, ~p"/registrations/email/#{token}") + assert redirected_to(conn) == ~p"/registrations/edit" assert Flash.get(conn.assigns.flash, :info) =~ "Email changed successfully" refute Users.get_user_by_email(user.email) assert Users.get_user_by_email(email) - conn = get(conn, Routes.registration_email_path(conn, :show, token)) - assert redirected_to(conn) == Routes.registration_path(conn, :edit) + conn = get(conn, ~p"/registrations/email/#{token}") + assert redirected_to(conn) == ~p"/registrations/edit" assert Flash.get(conn.assigns.flash, :error) =~ "Email change link is invalid or it has expired" end test "does not update email with invalid token", %{conn: conn, user: user} do - conn = get(conn, Routes.registration_email_path(conn, :show, "oops")) - assert redirected_to(conn) == Routes.registration_path(conn, :edit) + conn = get(conn, ~p"/registrations/email/oops") + assert redirected_to(conn) == ~p"/registrations/edit" assert Flash.get(conn.assigns.flash, :error) =~ "Email change link is invalid or it has expired" @@ -70,8 +70,8 @@ defmodule PhilomenaWeb.Registration.EmailControllerTest do test "redirects if user is not logged in", %{token: token} do conn = build_conn() - conn = get(conn, Routes.registration_email_path(conn, :show, token)) - assert redirected_to(conn) == Routes.session_path(conn, :new) + conn = get(conn, ~p"/registrations/email/#{token}") + assert redirected_to(conn) == ~p"/sessions/new" end end end diff --git a/test/philomena_web/controllers/registration/password_controller_test.exs b/test/philomena_web/controllers/registration/password_controller_test.exs index 3e216805a..b850e7af4 100644 --- a/test/philomena_web/controllers/registration/password_controller_test.exs +++ b/test/philomena_web/controllers/registration/password_controller_test.exs @@ -10,7 +10,7 @@ defmodule PhilomenaWeb.Registration.PasswordControllerTest do describe "PUT /registrations/password" do test "updates the user password and resets tokens", %{conn: conn, user: user} do new_password_conn = - put(conn, Routes.registration_password_path(conn, :update), %{ + put(conn, ~p"/registrations/password", %{ "current_password" => valid_user_password(), "user" => %{ "password" => "new valid password", @@ -18,7 +18,7 @@ defmodule PhilomenaWeb.Registration.PasswordControllerTest do } }) - assert redirected_to(new_password_conn) == Routes.registration_path(conn, :edit) + assert redirected_to(new_password_conn) == ~p"/registrations/edit" assert get_session(new_password_conn, :user_token) != get_session(conn, :user_token) assert Flash.get(new_password_conn.assigns.flash, :info) =~ "Password updated successfully" assert Users.get_user_by_email_and_password(user.email, "new valid password", & &1) @@ -26,7 +26,7 @@ defmodule PhilomenaWeb.Registration.PasswordControllerTest do test "does not update password on invalid data", %{conn: conn} do old_password_conn = - put(conn, Routes.registration_password_path(conn, :update), %{ + put(conn, ~p"/registrations/password", %{ "current_password" => "invalid", "user" => %{ "password" => "too short", @@ -34,7 +34,7 @@ defmodule PhilomenaWeb.Registration.PasswordControllerTest do } }) - assert redirected_to(old_password_conn) == Routes.registration_path(conn, :edit) + assert redirected_to(old_password_conn) == ~p"/registrations/edit" assert Flash.get(old_password_conn.assigns.flash, :error) =~ "Failed to update password" assert get_session(old_password_conn, :user_token) == get_session(conn, :user_token) end diff --git a/test/philomena_web/controllers/registration_controller_test.exs b/test/philomena_web/controllers/registration_controller_test.exs index 0ee59d907..dc5576664 100644 --- a/test/philomena_web/controllers/registration_controller_test.exs +++ b/test/philomena_web/controllers/registration_controller_test.exs @@ -5,13 +5,13 @@ defmodule PhilomenaWeb.RegistrationControllerTest do describe "GET /registrations/new" do test "renders registration page", %{conn: conn} do - conn = get(conn, Routes.registration_path(conn, :new)) + conn = get(conn, ~p"/registrations/new") html_response(conn, 200) end test "redirects if already logged in", %{conn: conn} do conn = - conn |> log_in_user(confirmed_user_fixture()) |> get(Routes.registration_path(conn, :new)) + conn |> log_in_user(confirmed_user_fixture()) |> get(~p"/registrations/new") assert redirected_to(conn) == "/" end @@ -23,7 +23,7 @@ defmodule PhilomenaWeb.RegistrationControllerTest do email = unique_user_email() conn = - post(conn, Routes.registration_path(conn, :create), %{ + post(conn, ~p"/registrations", %{ "user" => %{"name" => email, "email" => email, "password" => valid_user_password()} }) @@ -36,7 +36,7 @@ defmodule PhilomenaWeb.RegistrationControllerTest do test "render errors for invalid data", %{conn: conn} do conn = - post(conn, Routes.registration_path(conn, :create), %{ + post(conn, ~p"/registrations", %{ "user" => %{"email" => "with spaces", "password" => "too short"} }) @@ -50,15 +50,15 @@ defmodule PhilomenaWeb.RegistrationControllerTest do setup :register_and_log_in_user test "renders settings page", %{conn: conn} do - conn = get(conn, Routes.registration_path(conn, :edit)) + conn = get(conn, ~p"/registrations/edit") response = html_response(conn, 200) assert response =~ "Settings" end test "redirects if user is not logged in" do conn = build_conn() - conn = get(conn, Routes.registration_path(conn, :edit)) - assert redirected_to(conn) == Routes.session_path(conn, :new) + conn = get(conn, ~p"/registrations/edit") + assert redirected_to(conn) == ~p"/sessions/new" end end end diff --git a/test/philomena_web/controllers/session_controller_test.exs b/test/philomena_web/controllers/session_controller_test.exs index 78e148342..29d8d7f2d 100644 --- a/test/philomena_web/controllers/session_controller_test.exs +++ b/test/philomena_web/controllers/session_controller_test.exs @@ -9,12 +9,12 @@ defmodule PhilomenaWeb.SessionControllerTest do describe "GET /sessions/new" do test "renders log in page", %{conn: conn} do - conn = get(conn, Routes.session_path(conn, :new)) + conn = get(conn, ~p"/sessions/new") html_response(conn, 200) end test "redirects if already logged in", %{conn: conn, user: user} do - conn = conn |> log_in_user(user) |> get(Routes.session_path(conn, :new)) + conn = conn |> log_in_user(user) |> get(~p"/sessions/new") assert redirected_to(conn) == "/" end end @@ -22,7 +22,7 @@ defmodule PhilomenaWeb.SessionControllerTest do describe "POST /sessions" do test "logs the user in", %{conn: conn, user: user} do conn = - post(conn, Routes.session_path(conn, :create), %{ + post(conn, ~p"/sessions", %{ "user" => %{"email" => user.email, "password" => valid_user_password()} }) @@ -39,7 +39,7 @@ defmodule PhilomenaWeb.SessionControllerTest do test "logs the user in with remember me", %{conn: conn, user: user} do conn = - post(conn, Routes.session_path(conn, :create), %{ + post(conn, ~p"/sessions", %{ "user" => %{ "email" => user.email, "password" => valid_user_password(), @@ -53,7 +53,7 @@ defmodule PhilomenaWeb.SessionControllerTest do test "emits error message with invalid credentials", %{conn: conn, user: user} do conn = - post(conn, Routes.session_path(conn, :create), %{ + post(conn, ~p"/sessions", %{ "user" => %{"email" => user.email, "password" => "invalid_password"} }) @@ -64,7 +64,7 @@ defmodule PhilomenaWeb.SessionControllerTest do describe "DELETE /sessions" do test "logs the user out", %{conn: conn, user: user} do - conn = conn |> log_in_user(user) |> delete(Routes.session_path(conn, :delete)) + conn = conn |> log_in_user(user) |> delete(~p"/sessions") assert redirected_to(conn) == "/" refute get_session(conn, :user_token) assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully" diff --git a/test/philomena_web/controllers/unlock_controller_test.exs b/test/philomena_web/controllers/unlock_controller_test.exs index d506e90a6..bc8df85e4 100644 --- a/test/philomena_web/controllers/unlock_controller_test.exs +++ b/test/philomena_web/controllers/unlock_controller_test.exs @@ -12,7 +12,7 @@ defmodule PhilomenaWeb.UnlockControllerTest do describe "GET /unlocks/new" do test "renders the unlock page", %{conn: conn} do - conn = get(conn, Routes.unlock_path(conn, :new)) + conn = get(conn, ~p"/unlocks/new") response = html_response(conn, 200) assert response =~ "

Resend unlock instructions

" end @@ -22,7 +22,7 @@ defmodule PhilomenaWeb.UnlockControllerTest do @tag :capture_log test "sends a new unlock token", %{conn: conn, user: user} do conn = - post(conn, Routes.unlock_path(conn, :create), %{ + post(conn, ~p"/unlocks", %{ "user" => %{"email" => user.email} }) @@ -35,7 +35,7 @@ defmodule PhilomenaWeb.UnlockControllerTest do Repo.update!(Users.User.unlock_changeset(user)) conn = - post(conn, Routes.unlock_path(conn, :create), %{ + post(conn, ~p"/unlocks", %{ "user" => %{"email" => user.email} }) @@ -46,7 +46,7 @@ defmodule PhilomenaWeb.UnlockControllerTest do test "does not send unlock token if email is invalid", %{conn: conn} do conn = - post(conn, Routes.unlock_path(conn, :create), %{ + post(conn, ~p"/unlocks", %{ "user" => %{"email" => "unknown@example.com"} }) @@ -63,20 +63,20 @@ defmodule PhilomenaWeb.UnlockControllerTest do Users.deliver_user_unlock_instructions(user, url) end) - conn = get(conn, Routes.unlock_path(conn, :show, token)) + conn = get(conn, ~p"/unlocks/#{token}") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :info) =~ "Account unlocked successfully" refute Users.get_user!(user.id).locked_at refute get_session(conn, :user_token) assert Repo.all(Users.UserToken) == [] - conn = get(conn, Routes.unlock_path(conn, :show, token)) + conn = get(conn, ~p"/unlocks/#{token}") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :error) =~ "Unlock link is invalid or it has expired" end test "does not unlock with invalid token", %{conn: conn, user: user} do - conn = get(conn, Routes.unlock_path(conn, :show, "oops")) + conn = get(conn, ~p"/unlocks/oops") assert redirected_to(conn) == "/" assert Flash.get(conn.assigns.flash, :error) =~ "Unlock link is invalid or it has expired" assert Users.get_user!(user.id).locked_at diff --git a/test/philomena_web/user_auth_test.exs b/test/philomena_web/user_auth_test.exs index e03912fe5..deeaeca7f 100644 --- a/test/philomena_web/user_auth_test.exs +++ b/test/philomena_web/user_auth_test.exs @@ -162,7 +162,7 @@ defmodule PhilomenaWeb.UserAuthTest do test "redirects if user is not authenticated", %{conn: conn} do conn = conn |> fetch_flash() |> UserAuth.require_authenticated_user([]) assert conn.halted - assert redirected_to(conn) == Routes.session_path(conn, :new) + assert redirected_to(conn) == ~p"/sessions/new" assert Phoenix.Flash.get(conn.assigns.flash, :error) == "You must log in to access this page." From 885af363391de1ff7dab60717b4edb012eb37a18 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Thu, 6 Jun 2024 23:16:49 +0200 Subject: [PATCH 35/65] fix it up a tad --- .../templates/communication/_body.html.slime | 12 ++++++------ lib/philomena_web/templates/post/_post.html.slime | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/philomena_web/templates/communication/_body.html.slime b/lib/philomena_web/templates/communication/_body.html.slime index f2d293e1e..90b3cd947 100644 --- a/lib/philomena_web/templates/communication/_body.html.slime +++ b/lib/philomena_web/templates/communication/_body.html.slime @@ -1,6 +1,9 @@ -- anon = is_nil(assigns[:noanon]) or @noanon == false -- options = if is_nil(assigns[:options]), do: "", else: @options -- deleted = Map.has_key?(@object, :hidden_from_users) and @object.hidden_from_users == true +elixir: + anon = is_nil(assigns[:noanon]) or @noanon == false + options = if is_nil(assigns[:options]), do: "", else: @options + deleted = Map.has_key?(@object, :hidden_from_users) and @object.hidden_from_users == true + hidden_if_deleted = if deleted, do: "hidden", else: "" + deleted_class = if deleted, do: "communication--deleted", else: "" - avatar = cond do - not is_nil(assigns[:image]) -> @@ -48,9 +51,6 @@ - else =<> @body -- hidden_if_deleted = if deleted, do: "hidden", else: "" -- deleted_class = if deleted, do: "communication--deleted", else: "" - .flex.flex__grow.hidden--mobile class=deleted_class .flex.flex__fixed.spacing--right class=hidden_if_deleted = avatar diff --git a/lib/philomena_web/templates/post/_post.html.slime b/lib/philomena_web/templates/post/_post.html.slime index bdf7fd23d..ff516044e 100644 --- a/lib/philomena_web/templates/post/_post.html.slime +++ b/lib/philomena_web/templates/post/_post.html.slime @@ -1,7 +1,9 @@ -- post_link = ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}" +elixir: + post_link = ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}" + - options = if true do .flex__spacer.hidden--desktop - a.communication__interaction.hidden--desktop title="Link to post" href=post_link + a.communication__interaction.hidden--desktop title="Link to post" href="#{post_link}" i.fa.fa-link = if not is_nil(@post.edited_at) and can?(@conn, :show, @post) do a href=~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/history" From 90b4ee8915f46e9e71073bb954ddc449363eb7a8 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Mon, 10 Jun 2024 21:17:06 +0200 Subject: [PATCH 36/65] fix vite on external devices --- assets/vite.config.ts | 4 ++++ lib/philomena_web/templates/layout/app.html.slime | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/assets/vite.config.ts b/assets/vite.config.ts index c677a7ff6..9342c8895 100644 --- a/assets/vite.config.ts +++ b/assets/vite.config.ts @@ -28,6 +28,10 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { return { publicDir: 'static', plugins: [], + server: { + host: '0.0.0.0', + port: 5173, + }, resolve: { alias: { common: path.resolve(__dirname, 'css/common/'), diff --git a/lib/philomena_web/templates/layout/app.html.slime b/lib/philomena_web/templates/layout/app.html.slime index 996a37272..105b9b760 100644 --- a/lib/philomena_web/templates/layout/app.html.slime +++ b/lib/philomena_web/templates/layout/app.html.slime @@ -24,8 +24,8 @@ html lang="en" = csrf_meta_tag() = vite_hmr? do - script type="module" src="http://localhost:5173/@vite/client" - script type="module" src="http://localhost:5173/js/app.ts" + script type="module" src="http://#{@conn.host}:5173/@vite/client" + script type="module" src="http://#{@conn.host}:5173/js/app.ts" - else script type="text/javascript" src=~p"/js/app.js" async="async" = render PhilomenaWeb.LayoutView, "_opengraph.html", assigns From 13735e7d4e027dda4c4a763bb2bd36a8cb2ea333 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Mon, 10 Jun 2024 23:20:44 +0200 Subject: [PATCH 37/65] convert tags.js to ts --- assets/js/sources.ts | 4 ++-- assets/js/{tags.js => tags.ts} | 35 +++++++++++++++++++++------------- assets/js/tagsmisc.ts | 4 ++-- 3 files changed, 26 insertions(+), 17 deletions(-) rename assets/js/{tags.js => tags.ts} (64%) diff --git a/assets/js/sources.ts b/assets/js/sources.ts index b2c29c454..f1fbb88a9 100644 --- a/assets/js/sources.ts +++ b/assets/js/sources.ts @@ -1,7 +1,7 @@ import { $ } from './utils/dom'; import { inputDuplicatorCreator } from './input-duplicator'; -export interface SourcesEvent extends CustomEvent { +export interface TagSourceEvent extends CustomEvent { target: HTMLElement, } @@ -17,7 +17,7 @@ function setupInputs() { function imageSourcesCreator() { setupInputs(); - document.addEventListener('fetchcomplete', (({ target, detail }: SourcesEvent) => { + document.addEventListener('fetchcomplete', (({ target, detail }: TagSourceEvent) => { const sourceSauce = $('.js-sourcesauce'); if (sourceSauce && target && target.matches('#source-form')) { diff --git a/assets/js/tags.js b/assets/js/tags.ts similarity index 64% rename from assets/js/tags.js rename to assets/js/tags.ts index 1a97c196a..eaed02f7b 100644 --- a/assets/js/tags.js +++ b/assets/js/tags.ts @@ -2,23 +2,28 @@ * Tags Dropdown */ -import { showEl, hideEl } from './utils/dom'; +import { $$, showEl, hideEl } from './utils/dom'; +import { assertNotUndefined } from './utils/assert'; +import { TagSourceEvent } from './sources'; -function addTag(tagId, list) { +type TagDropdownActionFunction = () => void; +type TagDropdownActionList = Record; + +function addTag(tagId: number, list: number[]) { list.push(tagId); } -function removeTag(tagId, list) { +function removeTag(tagId: number, list: number[]) { list.splice(list.indexOf(tagId), 1); } -function createTagDropdown(tag) { +function createTagDropdown(tag: HTMLSpanElement) { const { userIsSignedIn, userCanEditFilter, watchedTagList, spoileredTagList, hiddenTagList } = window.booru; - const [ unwatch, watch, unspoiler, spoiler, unhide, hide, signIn, filter ] = [].slice.call(tag.querySelectorAll('.tag__dropdown__link')); - const [ unwatched, watched, spoilered, hidden ] = [].slice.call(tag.querySelectorAll('.tag__state')); - const tagId = parseInt(tag.dataset.tagId, 10); + const [ unwatch, watch, unspoiler, spoiler, unhide, hide, signIn, filter ] = $$('.tag__dropdown__link'); + const [ unwatched, watched, spoilered, hidden ] = $$('.tag__state'); + const tagId = parseInt(assertNotUndefined(tag.dataset.tagId), 10); - const actions = { + const actions: TagDropdownActionList = { unwatch() { hideEl(unwatch, watched); showEl(watch, unwatched); removeTag(tagId, watchedTagList); }, watch() { hideEl(watch, unwatched); showEl(unwatch, watched); addTag(tagId, watchedTagList); }, @@ -51,11 +56,15 @@ function createTagDropdown(tag) { if (userIsSignedIn && !userCanEditFilter) showEl(filter); - tag.addEventListener('fetchcomplete', event => actions[event.target.dataset.tagAction]()); -} + tag.addEventListener('fetchcomplete', ((event: TagSourceEvent) => { + const act = event.target.dataset.tagAction; -function initTagDropdown() { - [].forEach.call(document.querySelectorAll('.tag.dropdown'), createTagDropdown); + if (act && actions[act]) { + actions[act]() + } + }) as EventListener); } -export { initTagDropdown }; +export function initTagDropdown() { + [].forEach.call($$('.tag.dropdown'), createTagDropdown); +} diff --git a/assets/js/tagsmisc.ts b/assets/js/tagsmisc.ts index ea5f49762..e9882baed 100644 --- a/assets/js/tagsmisc.ts +++ b/assets/js/tagsmisc.ts @@ -6,7 +6,7 @@ import { $, $$ } from './utils/dom'; import store from './utils/store'; import { initTagDropdown } from './tags'; import { setupTagsInput, reloadTagsInput } from './tagsinput'; -import { SourcesEvent } from './sources'; +import { TagSourceEvent } from './sources'; type TagInputActionFunction = (tagInput: HTMLTextAreaElement | null) => void type TagInputActionList = { @@ -49,7 +49,7 @@ function setupTags() { }); } -function updateTagSauce({target, detail}: SourcesEvent) { +function updateTagSauce({target, detail}: TagSourceEvent) { const tagSauce = $('.js-tagsauce'); if (tagSauce && target.matches('#tags-form')) { From 4e6665e2d6f79dc42da2258d02c86b22c74e3820 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Tue, 11 Jun 2024 01:01:02 +0200 Subject: [PATCH 38/65] notification badges, navbar + admin navbar --- assets/css/common/measurements.css | 13 ++++-- assets/css/common/mixins.css | 6 +++ assets/css/elements/mobile.css | 15 +++++++ assets/css/views/admin.css | 35 ++++++++++++--- assets/css/views/header.css | 43 +++++++++++++++++++ assets/css/views/interaction.css | 2 +- assets/js/notifications.js | 3 +- assets/js/tags.ts | 2 +- .../templates/layout/_header.html.slime | 12 ++++-- .../layout/_header_navigation.html.slime | 2 +- ...me => _header_navigation_staff.html.slime} | 34 +++++++++++---- 11 files changed, 142 insertions(+), 25 deletions(-) rename lib/philomena_web/templates/layout/{_header_staff_links.html.slime => _header_navigation_staff.html.slime} (85%) diff --git a/assets/css/common/measurements.css b/assets/css/common/measurements.css index 3ef1035ed..81a7119c2 100644 --- a/assets/css/common/measurements.css +++ b/assets/css/common/measurements.css @@ -1,3 +1,4 @@ +$min-widescreen-width: 1500px; $min-desktop-width: 1280px; $max-phone-width: 700px; $narrow-layout-width: 960px; @@ -22,7 +23,7 @@ $font-family-heading: $font-family-base; /* Navbar */ --navbar-size: 4rem; - --navbar-secondary-size: 2.5rem; + --navbar-secondary-size: 2.25rem; --navbar-input-size: 2rem; --navbar-input-width: 18vw; --navbar-input-max-width: 22rem; @@ -76,11 +77,14 @@ $font-family-heading: $font-family-base; --badge-normal-size: 2rem; /* Counter badges (the "red notification badge") */ - --number-badge-size: 0.6rem; + --number-badge-size: 0.8rem; --number-badge-padding: 0.1rem; --number-badge-border: 2px; - --number-badge-lower-offset: 0.7rem; - --number-badge-upper-offset: 0.7rem; + --number-badge-vote-vertical-offset: 0.7rem; + --number-badge-header-vertical-offset: 1.2rem; + --number-badge-header-horizontal-offset: 0.6rem; + --number-badge-admin-vertical-offset: 0.6rem; + --number-badge-admin-horizontal-offset: 0.6rem; /* Fonts */ --font-family: $font-family-base; @@ -91,6 +95,7 @@ $font-family-heading: $font-family-base; --font-micro-size: 10px; --font-header-size: 16px; --font-icon-size: 2rem; + --font-small-icon-size: 0.9rem; --font-h1-size: 1.75rem; --font-h2-size: 1.5rem; --font-h3-size: 1.35rem; diff --git a/assets/css/common/mixins.css b/assets/css/common/mixins.css index 880dcccdc..ec72599ca 100644 --- a/assets/css/common/mixins.css +++ b/assets/css/common/mixins.css @@ -1,3 +1,9 @@ +@define-mixin if-widescreen { + @media (min-width: calc($min-widescreen-width + 1px)) { + @mixin-content; + } +} + @define-mixin if-desktop { @media (min-width: calc($min-desktop-width + 1px)) { @mixin-content; diff --git a/assets/css/elements/mobile.css b/assets/css/elements/mobile.css index 19c6a569c..e89fbe051 100644 --- a/assets/css/elements/mobile.css +++ b/assets/css/elements/mobile.css @@ -1,3 +1,7 @@ +.hidden--narrow-screen { + display: none; +} + /* Phones. */ @mixin if-phone { .hidden--phone { @@ -25,3 +29,14 @@ display: none !important; } } + +/* Widescreens are larger than 1500px */ +@mixin if-widescreen { + .hidden--widescreen { + display: none !important; + } + + .hidden--narrow-screen { + display: inherit; + } +} diff --git a/assets/css/views/admin.css b/assets/css/views/admin.css index 476684578..25d182374 100644 --- a/assets/css/views/admin.css +++ b/assets/css/views/admin.css @@ -1,12 +1,37 @@ +nav.header__staff { + display: flex; + line-height: var(--navbar-secondary-size); + border-radius: var(--border-radius-outer); +} + .header__counter__admin { - position: absolute; + display: inline-flex; + flex-flow: column; background: var(--danger-color); - border: 1px solid var(--danger-border-color); - border-radius: var(--border-radius-inner); + border-radius: 100%; min-width: var(--number-badge-size); line-height: var(--number-badge-size); font-size: var(--number-badge-size); + font-weight: bold; padding: var(--number-badge-padding); - top: var(--number-badge-lower-offset); - left: 0.25rem; + text-align: center; +} + +.header--secondary__admin-links { + display: flex; + gap: var(--padding-large); + padding: 0 var(--padding-normal); + background: var(--danger-dark-color); + border-radius: var(--border-radius-outer); +} + +@mixin if-mobile { + .header--secondary__admin-links { + justify-content: center; + flex: 1 0 auto; + } + + nav.header__staff { + flex: 1 0 auto; + } } diff --git a/assets/css/views/header.css b/assets/css/views/header.css index b681c4f2b..96dc8cc7f 100644 --- a/assets/css/views/header.css +++ b/assets/css/views/header.css @@ -9,6 +9,7 @@ header { .header__link { @mixin animated-transition; + position: relative; color: var(--text-color) !important; } @@ -42,6 +43,29 @@ header { border: 0; } +.header__counter { + position: absolute; + background: var(--danger-color); + border-radius: 100%; + min-width: var(--number-badge-size); + line-height: var(--number-badge-size); + font-size: var(--number-badge-size); + font-weight: bold; + padding: var(--number-badge-padding); + text-align: center; + top: var(--number-badge-header-vertical-offset); + left: var(--number-badge-header-horizontal-offset); +} + +/* Old style counter */ +.header__counter--simple { + display: inline-flex; + padding: 0 var(--padding-small); + line-height: inherit; + text-align: center; + background: var(--primary-muted-color); +} + .header__input { @mixin animated-transition; height: var(--navbar-input-size); @@ -72,9 +96,18 @@ header { padding: 0 var(--padding-normal); } +.nav-container { + display: flex; + gap: var(--padding-normal); + padding: 0 var(--padding-normal); + flex-wrap: wrap; +} + nav.header__secondary { display: flex; + flex: 1 0 auto; line-height: var(--navbar-secondary-size); + border-radius: var(--border-radius-outer); padding: 0 var(--padding-large); font-size: var(--font-header-size); gap: var(--padding-large); @@ -82,6 +115,16 @@ nav.header__secondary { background: var(--primary-color); } +nav.header__secondary .fa, +nav.header__staff .fa { + display: inline-flex; + text-align: center; + font-size: var(--font-small-icon-size); + width: var(--font-small-icon-size); + height: var(--font-small-icon-size); + margin-right: var(--padding-tiny); +} + nav.header__secondary .header__link { color: var(--text-light-color) !important; } diff --git a/assets/css/views/interaction.css b/assets/css/views/interaction.css index aa4daeee3..e3eeae100 100644 --- a/assets/css/views/interaction.css +++ b/assets/css/views/interaction.css @@ -27,7 +27,7 @@ padding: var(--number-badge-padding); line-height: var(--number-badge-size); min-width: var(--number-badge-size); - top: var(--number-badge-lower-offset); + top: var(--number-badge-vote-vertical-offset); } .interaction--comment:hover > .comments-count { diff --git a/assets/js/notifications.js b/assets/js/notifications.js index 04d48f91a..021678580 100644 --- a/assets/js/notifications.js +++ b/assets/js/notifications.js @@ -5,6 +5,7 @@ import { fetchJson, handleError } from './utils/requests'; import { $ } from './utils/dom'; import { delegate } from './utils/events'; +import { assertNotNull } from './utils/assert'; import store from './utils/store'; const NOTIFICATION_INTERVAL = 600000, @@ -39,7 +40,7 @@ function getNewNotifications() { } function updateNotificationTicker(notificationCount) { - const ticker = $('.js-notification-ticker'); + const ticker = assertNotNull($('.js-notification-ticker')); const parsedNotificationCount = Number(notificationCount); ticker.dataset.notificationCount = parsedNotificationCount; diff --git a/assets/js/tags.ts b/assets/js/tags.ts index eaed02f7b..c7be4c886 100644 --- a/assets/js/tags.ts +++ b/assets/js/tags.ts @@ -60,7 +60,7 @@ function createTagDropdown(tag: HTMLSpanElement) { const act = event.target.dataset.tagAction; if (act && actions[act]) { - actions[act]() + actions[act](); } }) as EventListener); } diff --git a/lib/philomena_web/templates/layout/_header.html.slime b/lib/philomena_web/templates/layout/_header.html.slime index 4aa57010b..c08590e59 100644 --- a/lib/philomena_web/templates/layout/_header.html.slime +++ b/lib/philomena_web/templates/layout/_header.html.slime @@ -30,7 +30,7 @@ header = if @current_user do a.header__link href="/notifications" title="Notifications" i.fas.fa-bell> - /span.js-notification-ticker.fa__text.header__counter data-notification-count=@notification_count = @notification_count + span.js-notification-ticker.fa__text.header__counter data-notification-count=@notification_count = @notification_count a.header__link href="/conversations" title="Conversations" = if @conversation_count > 0 do @@ -125,7 +125,11 @@ header | Register a.header__link href=~p"/sessions/new" | Login -nav.header__secondary - = render PhilomenaWeb.LayoutView, "_header_navigation.html", assigns + +.nav-container + nav.header__secondary + = render PhilomenaWeb.LayoutView, "_header_navigation.html", assigns + = if !is_nil(@current_user) and @current_user.role != "user" do - /= render PhilomenaWeb.LayoutView, "_header_staff_links.html", assigns + nav.header__staff + = render PhilomenaWeb.LayoutView, "_header_navigation_staff.html", assigns diff --git a/lib/philomena_web/templates/layout/_header_navigation.html.slime b/lib/philomena_web/templates/layout/_header_navigation.html.slime index dff5426a4..84913433d 100644 --- a/lib/philomena_web/templates/layout/_header_navigation.html.slime +++ b/lib/philomena_web/templates/layout/_header_navigation.html.slime @@ -45,7 +45,7 @@ a.header__link href="/tags" a.header__link href="/channels" i.fa.fa-stream> ' Live - span.header__counter + span.header__counter--simple = @conn.assigns.live_channels a.header__link href="/galleries" diff --git a/lib/philomena_web/templates/layout/_header_staff_links.html.slime b/lib/philomena_web/templates/layout/_header_navigation_staff.html.slime similarity index 85% rename from lib/philomena_web/templates/layout/_header_staff_links.html.slime rename to lib/philomena_web/templates/layout/_header_navigation_staff.html.slime index 8543fa076..7b9300425 100644 --- a/lib/philomena_web/templates/layout/_header_staff_links.html.slime +++ b/lib/philomena_web/templates/layout/_header_navigation_staff.html.slime @@ -1,8 +1,8 @@ -.flex.flex--centered.header--secondary__admin-links.stretched-mobile-links.js-staff-action +.header--secondary__admin-links.js-staff-action = if manages_users?(@conn) do .dropdown.hidden--mobile a.header__link title="Admin" - ' A + i.fa.fa-bars> i.fa.fa-caret-down .dropdown__content.js-burger-links @@ -64,38 +64,56 @@ = if @pending_approval_count do = link to: ~p"/admin/approvals", class: "header__link", title: "Approval Queue" do - ' Q + i.fa.fa-stamp> + span + | A + span.hidden--narrow-screen> pprovals span.header__counter__admin = @pending_approval_count = if @duplicate_report_count do = link to: ~p"/duplicate_reports", class: "header__link", title: "Duplicates" do - ' D + i.fa.fa-copy> + span + | D + span.hidden--narrow-screen> upes span.header__counter__admin = @duplicate_report_count = if @report_count do = link to: ~p"/admin/reports", class: "header__link", title: "Reports" do - ' R + i.fa.fa-flag> + span + | R + span.hidden--narrow-screen> eports span.header__counter__admin = @report_count = if @artist_link_count do = link to: ~p"/admin/artist_links", class: "header__link", title: "Artist Links" do - ' L + i.fa.fa-link> + span + | L + span.hidden--narrow-screen> inks span.header__counter__admin = @artist_link_count = if @dnp_entry_count do = link to: ~p"/admin/dnp_entries", class: "header__link", title: "DNP Requests" do - ' S + i.fa.fa-circle-minus> + span + span.hidden--narrow-screen> DNPs + span.hidden--widescreen> S span.header__counter__admin = @dnp_entry_count = if manages_bans?(@conn) do .dropdown.hidden--mobile a.header__link title="Bans" - ' B + i.fa.fa-gavel> + span + | B + span.hidden--narrow-screen> ans i.fa.fa-caret-down .dropdown__content.dropdown__content--right.js-burger-links From 653235d47da1e4d176da0fa9091ec3300c82b3ff Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 12 Jun 2024 20:11:43 +0200 Subject: [PATCH 39/65] improve dev seeds (wip) --- priv/repo/seeds.exs | 2 +- priv/repo/seeds/dev/communications.json | 58 ++ priv/repo/seeds/dev/images.json | 388 ++++++++ priv/repo/seeds/dev/pages.json | 70 ++ priv/repo/seeds/dev/users.json | 32 + priv/repo/seeds/pages/about.md | 7 + priv/repo/seeds/pages/advertising.md | 11 + priv/repo/seeds/pages/api.md | 1156 +++++++++++++++++++++++ priv/repo/seeds/pages/approval.md | 43 + priv/repo/seeds/pages/contact.md | 1 + priv/repo/seeds/pages/donations.md | 1 + priv/repo/seeds/pages/faq.md | 1 + priv/repo/seeds/pages/markdown.md | 395 ++++++++ priv/repo/seeds/pages/privacy.md | 1 + priv/repo/seeds/pages/rules.md | 0 priv/repo/seeds/pages/search_syntax.md | 1 + priv/repo/seeds/pages/shortcuts.md | 1 + priv/repo/seeds/pages/spoilers.md | 1 + priv/repo/seeds/pages/start.md | 1 + priv/repo/seeds/pages/tags.md | 1 + priv/repo/seeds/pages/takedowns.md | 1 + priv/repo/seeds/pages/uploading.md | 1 + priv/repo/{ => seeds}/seeds.json | 2 +- priv/repo/seeds/seeds_development.json | 2 + priv/repo/seeds_development.exs | 319 +++++-- priv/repo/seeds_development.json | 444 --------- 26 files changed, 2396 insertions(+), 544 deletions(-) create mode 100644 priv/repo/seeds/dev/communications.json create mode 100644 priv/repo/seeds/dev/images.json create mode 100644 priv/repo/seeds/dev/pages.json create mode 100644 priv/repo/seeds/dev/users.json create mode 100644 priv/repo/seeds/pages/about.md create mode 100644 priv/repo/seeds/pages/advertising.md create mode 100644 priv/repo/seeds/pages/api.md create mode 100644 priv/repo/seeds/pages/approval.md create mode 100644 priv/repo/seeds/pages/contact.md create mode 100644 priv/repo/seeds/pages/donations.md create mode 100644 priv/repo/seeds/pages/faq.md create mode 100644 priv/repo/seeds/pages/markdown.md create mode 100644 priv/repo/seeds/pages/privacy.md create mode 100644 priv/repo/seeds/pages/rules.md create mode 100644 priv/repo/seeds/pages/search_syntax.md create mode 100644 priv/repo/seeds/pages/shortcuts.md create mode 100644 priv/repo/seeds/pages/spoilers.md create mode 100644 priv/repo/seeds/pages/start.md create mode 100644 priv/repo/seeds/pages/tags.md create mode 100644 priv/repo/seeds/pages/takedowns.md create mode 100644 priv/repo/seeds/pages/uploading.md rename priv/repo/{ => seeds}/seeds.json (98%) create mode 100644 priv/repo/seeds/seeds_development.json delete mode 100644 priv/repo/seeds_development.json diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 1fe20dcd8..58a20bd83 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -40,7 +40,7 @@ for model <- [Image, Comment, Gallery, Tag, Post, Report, Filter] do end resources = - "priv/repo/seeds.json" + "priv/repo/seeds/seeds.json" |> File.read!() |> Jason.decode!() diff --git a/priv/repo/seeds/dev/communications.json b/priv/repo/seeds/dev/communications.json new file mode 100644 index 000000000..43e235660 --- /dev/null +++ b/priv/repo/seeds/dev/communications.json @@ -0,0 +1,58 @@ +{ + "demos": [ + "bold is **bold**, italic is _italic_, spoiler is ||spoiler||, code is `code`, underline is __underline__, strike is ~~strike~~, sup is ^sup^, sub is %sub%.", + "inline embedded thumbnails (tsp): >>1t >>1s >>1p", + "embedded image inside a spoiler: ||who needs it anyway >>1s||", + "spoilers inside of a table\n\nHello | World\n--- | ---:\n`||cool beans!||` | ||cool beans!||" + ], + "random": [ + "Oh my gosh, foxes are just too adorable, aren't they? With their pointy little ears, fluffy tails, and mischievous grins, they look like they've just stepped out of a fairy tale. And the way they prance and play in the wild, you can't help but be charmed by their antics. They have such a wide range of colors too, from vibrant reds to cool grays and even that stunning arctic white. Each one looks like it could be the star of its own animated movie. Honestly, watching videos of them jumping around and making those cute little fox sounds can totally make my day!", + "Wolves are absolutely fascinating creatures, and their greatness really speaks for itself! They embody the wild spirit of nature with their fierce eyes, powerful builds, and majestic howls that echo through the wilderness. It's incredible how these animals operate within sophisticated social structures, showing deep loyalty and intricate communication within their packs. Their roles as apex predators help maintain the balance of their ecosystems, managing populations of other animals and thereby supporting overall biodiversity. Plus, observing their interactions and tactical hunting strategies really highlights their intelligence and adaptability. Wolves definitely deserve our respect and admiration for their vital role in the natural world.", + "Frontend JavaScript frameworks are running rampant and it's high time we talk about the chaos they're creating! Honestly, it feels like a new framework pops up every other day, each one promising to be the solution to all our development woes. Yet, what we end up with is massive complexity, steep learning curves, and interminable \"best practices\" that shift faster than you can say \"deprecated.\" Developers are forced to hop from one framework to another, which dilutes focus and fragments the community. Can we please just settle on a few solid, well-supported options rather than continually chasing the next shiny thing? It's exhausting and not sustainable. The JavaScript community would be much better served by focusing on enhancing and stabilizing the frameworks we already have, rather than pushing out yet another contender in this already overcrowded space.", + "Elixir and the Phoenix framework truly are game changers in the world of programming, especially for those looking to build highly scalable and maintainable applications. Elixir, built on the reliable Erlang VM, provides exceptional concurrency and fault tolerance, making it an outstanding choice for real-time systems. Its syntax is clean and modern, which greatly simplifies the process of writing maintainable code. When you pair Elixir with Phoenix, things get even better. Phoenix leverages the strengths of Elixir to offer impressive speeds and unparalleled responsiveness in web development. It also comes with built-in support for WebSockets, which is a blessing for building interactive, real-time applications. The community around these technologies is vibrant and supportive, constantly contributing to a growing ecosystem of packages and tools. For developers looking for a robust solution for high-performance applications, diving into Elixir and Phoenix is a no-brainer!", + "Oh, please, there's simply no contest when it comes to operating systems; Arch Linux stands leagues above the rest. I mean, if you're serious about understanding how your system works, or if you actually value control and efficiency over hand-holding and spoon-feeding, then Arch is the clear choice. Its rolling release model ensures that you are always at the bleeding edge of technology—none of this outdated, \"stable release\" nonsense. And the AUR? An unmatched repository where the breadth and depth of available packages laugh in the face of other so-called \"advanced\" distributions. Setting up an Arch system might be a day's work for a novice, but for those who excel, it's a testament to skill and a badge of honor. They say “you must be this smart to use Arch,” and frankly, it's true. Why settle for less when you can have the purity of a user-centric, endlessly customizable experience? Let the uninitiated toil with their GUI installers and automatic setups; I'll be over here, crafting a sleek, efficient system that does exactly what I need, how I need it, when I need it.", + "i use arch btw", + "Absolutely ridiculous! Here I am with a supposedly high-end PC that I paid a good chunk of money for, and what happens? It literally starts smoking out of nowhere while I'm in the middle of an important task! I mean, you'd think with all the advances in technology these days, at least \"non-combustibility\" would be a feature you can count on. Now I'm stuck dealing with potential data loss, hardware damage, and not to mention the sheer inconvenience of it all. And you better believe I'm going to be on the phone with customer support who, I bet, will try to pin this on anything but a flaw in their product. Seriously, how hard is it to make a computer that doesn't turn into a fireplace? Everyone's going digital, and yet here we are, it seems like I've set up camp at the bonfire. Unbelievable!", + "Absolutely unbelievable! You won't believe this, but the latest update just \"fixed\" the one bug I was actually benefiting from! Holding down the spacebar on my computer used to cause unusually high CPU usage, which, yes, led to it heating up, but hear me out – during the winter, this bug was my personal, little makeshift heater! It was perfect; I could work on my stuff while keeping my room cozy without cranking up the costly central heating. But now, with this latest patch, the developers have “corrected” what they deemed a bug, and just like that, my delightful little heater feature is gone. My room feels like a freezer again while I type. Seriously, what were they thinking? It's ridiculous that fixing bugs also means stripping away the unintended perks. This was a feature, not a bug, from my perspective! Bring back my CPU-heating spacebar!", + "Hey everyone! I'm on the hunt for some fellow adventurers to dive into the realms of Dungeons & Dragons. Whether you're a seasoned Dungeon Master or a curious newbie ready to take your first steps into role-playing, I'm super excited to get a group together. We can explore mysterious dungeons, battle fearsome dragons, and undertake epic quests that will be talked about for ages! I'm open to any campaign setting - whether it's navigating the political intrigue of Waterdeep or braving the untamed wilderness of Chult. Ideally, I'd love to meet weekly, either online using platforms like Roll20 or Discord, or in person if you're nearby. So grab your dice, prepare your character sheets, and let's create some unforgettable stories together. Who's with me?", + "Ah, the quest for the Holy Grail of digital art software! Where do I even begin? I've been on a wild goose chase, bouncing from program to program—starting with Draw-o-Matic, hopping over to SketchySketch, and even dabbling in the enigmatic realms of PixelPerfect Pro. Each promises to turn my clumsy scribbles into strokes of genius, yet somehow, my digital doodling still looks like abstract art gone wrong. It's like choosing between different brands of spaghetti noodles...in the end, can you really tell them apart when they're all covered in sauce?\n\nLet's be real; after my millionth failed attempt at mastering brushes and layers, I decided that true artistic talent might just be out of my reach. So, guess what? I've thrown in the traditional artist's towel and embraced our futuristic overlords – yes, AI art generators! Why struggle with pen tablets and complicated controls when a clever algorithm can whip up a masterpiece with the click of a button? So here's to letting AI do the heavy lifting while I sit back, sip my coffee, and marvel at the magic of machine-made art. Who needs skill when you have technology, right? Cheers to the easy art life!", + "Cooking breakfast, supposedly the most important meal of the day, can feel like an insurmountable chore. First, there's the waking up part, which involves peeling yourself from the comforting embrace of your bed at an ungodly hour. Then, you trudge to the kitchen, where a gauntlet of tasks awaits. You start with the basics: eggs. But the eggs don't just hop out of the shell — oh no, you have to crack them just right or else you'll be fishing out pieces of shell for what feels like hours. And let's not even get started on getting the cooking temperature perfect; too hot, and they're rubber, too cool, and they just sit there, mocking you.\n\nMeanwhile, the bread quietly burns in the toaster as you juggle pots and spatulas. And cereal? It might seem easy until you realize you're out of milk — a discovery typically made only after you've already filled your bowl. Add to that the high stakes of brewing a decent cup of coffee, which is practically a dark art involving precise timings and temperatures. By the end of this so-called 'simple' task, the kitchen looks like a cyclone hit it, you're exhausted, and it's only the start of your day. Sometimes, it seems like breakfast is just not worth the hassle.", + "Oh, PHP, where do I even begin with this relic from the early days of the web? From its inconsistent function names to a bewildering mix of underscores and camelCase, PHP often feels like a language playing a cruel joke on developers. Its error handling is a nightmare straight out of a coder's worst dreams, where one minute you think everything's running smoothly, and the next, you're greeted by a blank screen with no clues. Then there's the infamous 'PHP spaghetti code', a term so closely associated with PHP that they might as well trademark it. The language allows, even promotes, practices that would make any seasoned developer cringe, muddling logic and presentation in ways that other, more refined languages have long since evolved past.\n\nAnd let's not gloss over the security aspects—PHP makes it frighteningly easy to write code with major vulnerabilities, such as SQL injection and XSS, practically inviting hackers to a free-for-all buffet. Despite its efforts to improve with newer versions, PHP carries the burden of its chaotic past, a Frankenstein's monster of a scripting language that just can't shake off its jumbled roots. In the fast-paced world of modern web development, clinging to PHP feels like driving a rickety old car that's always one bump away from falling apart. Why suffer through the eccentricities of PHP when there are so many cleaner, more robust alternatives waiting in the wings?", + "Oh man, I've got this unstoppable craving for chicken wings! You know, nothing beats the irresistible allure of crispy, perfectly seasoned wings that just melt off the bone. Whether they're doused in a fiery hot sauce or coated in a sweet and savory glaze, each bite is a little piece of heaven. It's not just about satisfying hunger; it's an experience, a guilty pleasure that calls for piles of napkins and not caring about how messy your fingers get. Is there anything better than tucking into a mountain of these glorious wings while zoning out to your favorite show or hanging out with friends? I think not. Now, if only I could telepathically summon a plate full of them right now, that would be the dream. Seriously, anyone know where I can get the best wings delivered ASAP? Because this craving isn't going to satisfy itself!", + "You wouldn't believe it, but I just witnessed the most breathtaking art of my life! It was one of those rare, soul-stirching moments when you feel completely transported by the power of creativity. The pieces were a masterful blend of colors and textures that seemed to dance together, telling a story deeper than words ever could. Each stroke had its own pulse, vibrant with life and emotion, pulling me into a world crafted by the artist's hands. The way the light played across the canvas made it seem almost alive, as if the scenes depicted were unfolding right before my eyes. I was totally captivated, lost in the beauty and complexity of it all. It wasn't just art; it was a profound experience that left me reflecting long after I walked away. How incredible is the human ability to convey such depth and feeling through mere brushes and pigments? I'm absolutely inspired and reminded of why art is so essential to our lives.", + "Ugh, I've got to admit, the patience is wearing thin with this one user in our thread. They keep missing the point and throwing off the whole discussion with tangents that aren't exactly relevant. Just when we start getting into the meat of the topic, in they come with off-the-wall comments that grind everything to a halt. It's like they're in their own little world! I've tried steering the conversation back on track a few times, but it's like talking to a brick wall. Honestly, it's becoming a real test of my forum etiquette to keep responding politely. Maybe it's time for some subtle hints or a direct message to let them know they're derailing the discourse a bit too much?", + "Honestly, scrolling through this thread feels like I've accidentally stumbled into some kind of bizarre, AI-generated text adventure. Each comment seems more random than the last, barely connected to the one before it, like a patchwork quilt made by a robot who's only just learned about human conversation. It's almost amusing, trying to follow the logic as it hops from topic to topic with surreal, dream-like transitions. It makes you wonder if anyone's actually steering this ship, or if we're all just passengers on an auto-pilot journey through Randomville. While it's been an interesting ride, a little coherence would be nice. Maybe it's time to tighten up the topic or set some ground rules for clarity's sake!", + "A jet engine works by following the basic principle of thrust generation through Newton's third law of motion: for every action, there is an equal and opposite reaction. It begins with air intake at the front of the engine. This air is compressed by a series of fan blades, raising the air's pressure and temperature. Next, the high-pressure air enters a combustion chamber where it is mixed with fuel and ignited. The resulting high-temperature gases expand rapidly and are forced out through the rear of the engine at high speed. This expulsion of gases generates thrust, propelling the engine (and the aircraft) forward. The engine also includes turbines located after the combustion chamber, which are driven by the outgoing gases. These turbines are connected to the front fans and compressors via a shaft and help in powering them, making the process self-sustaining.", + "Can you believe it? I've been banned from my favorite forum, and let me tell you, the moderators handled it terribly. It's like they're on a power trip or something! There was hardly any warning or proper communication about why my posts were suddenly considered problematic. They just slapped a ban on my account with some generic reasoning about violating community guidelines. Talk about frustrating! It feels completely unfair, especially since I've contributed so much to that forum over the years. Their approach just killed all the camaraderie and dynamic discussions we had going. Seriously, those mods need to learn a thing or two about handling issues with a bit more tact and clarity. Guess it's time to find a new online hangout where the moderators actually appreciate their community members!", + "Well, would you look at that? I got booted from the old forum—apparently, I'm too much of a free spirit for their strict standards! But no matter, I've discovered this new, glorious haven. The moderation here is just chef's kiss—so lenient, so understanding, practically the Wild West compared to that stuffy old forum!\n\nIt's hilarious, really. Here, the moderators actually get my sense of humor and my, uh, vibrant debate style. They seem to appreciate a good controversial topic or seven, unlike those other mods who couldn't handle a little spirited discussion. At my new online home, not only can I express my 'unique' opinions freely, but it seems like almost anything goes! Who would've thought that finding a place where the rules are just guidelines would be so refreshing?\n\nI mean, it's clear now—the problem was definitely them, not me. It's almost like this new forum was made for me. Here's to hoping they continue to see the genius in my chaos!", + "Five whole seconds for a moderator response? Absolutely outrageous! In this digital age, five seconds feels like an eternity. I mean, what could they possibly be doing that's so important? It's almost as if they have lives outside of moderating this forum! Who knew that awaiting a reply could stretch into such an unbearable timeframe? It's almost comical how these delays test my patience. I could have brewed a cup of tea in that time—or at least microwaved a cup of water. It's time for a revolution in moderator response times! They need to understand that in the fast-paced world of the internet, every second counts. Let's usher in an era of instant gratification!", + "Honestly, at this point, why do we even bother with human moderators? They tire, they need breaks, and don't get me started on the personal biases! It's the 21st century, and it's high time we embraced the efficiency of AI moderation. Imagine a world where responses are instantaneous, no waiting for a moderator to finish their coffee break. An AI moderator wouldn't sleep, wouldn't take vacations, and certainly wouldn't bring any of those pesky human emotions or biases to the table.\n\nAnd consistency! Oh, the consistency we'd experience with AI—no more of this \"depends on who you get\" roulette. Every decision would be based on cold, hard logic and predefined protocols. Sure, some might argue that AI lacks the human touch, might be too rigid, or could misinterpret nuances, but I say it's a tradeoff worth exploring. Let's streamline this whole process and bring in AI that can keep up with the speed of our posts and the scale of our debates. Out with the old, in with the new—AI moderators for a faster, bias-free forum experience!", + "I just have to vent about this— the staff on this website are just unbearable! It feels like every interaction is an exercise in frustration. They move at the pace of a snail, always lagging days behind when you need urgent help. It's as if they've never heard of customer service. And their knowledge? Seems like they're always 'looking into it' or 'will get back to you shortly,' which translates to an eternity of waiting with no real answers.\n\nDon't even get me started on the technical issues. Every little glitch or bug, and their best advice? \"Try refreshing your browser\" or \"clear your cookies.\" Genius solutions, really, groundbreaking stuff. It's like they're just regurgitating lines from a poorly written script. And heaven forbid you have a real, complex issue; you might as well talk to a wall.\n\nPlus, their attitude leaves much to be desired. There's this air of indifference, as if your problems are just minor annoyances to them rather than legitimate concerns. Honestly, a bunch of unenthused, unengaged robots could probably do a better job. At least robots wouldn't have an attitude. They seriously need to up their game or consider a major overhaul of their staff because this level of service just isn't cutting it.", + "Oh, isn't it just delightful to watch those adorable little birds flit around? Their tiny beaks, the variety of vibrant colors, and those fluttery, delicate wings just fill the heart with joy. Whether they're darting delicately from branch to branch or chirping sweetly in the morning light, each bird seems to have its own charming personality. And let's not forget when they puff up into fluffy balls to keep warm – absolutely precious! Observing birds can easily brighten anyone's day, whether it's a city pigeon strutting with unexpected elegance or a brilliantly colored parrot speaking mimicry. Their playful antics and endearing behaviors are a constant reminder of nature's simple beauties and the intricate marvels of wildlife. Watching them go about their busy lives, one can't help but be enamored by their cuteness and fascinating lives.", + "Cats are just the epitome of cuteness! From their mesmerizing eyes that seem to change with their moods to their small, expressive faces framed by those adorable twitching whiskers, every aspect of a cat can tug at the heartstrings. And who can resist the charm of a cat's gentle purr, a sound that signifies the utmost contentment and can soothe any soul within hearing distance? Cats are also masters of flexibility and grace, moving with an elegance that is both impressive and endearing. Whether they're meticulously grooming themselves, curiously pawing at a dangling string, or arching their backs in that classic stretch, every action seems designed to reaffirm their status as delightful companions. Plus, their love for cozy naps in sunny spots or curled up in laps just makes them all the more adorable. Truly, cats with their quirky, independent personalities and cute antics, are a constant source of joy and amusement.", + "The printer perches regally on the windowsill, silently surveying its domain with alert, watchful eyes. Its sleek form is compact yet sturdy, often covered in hues ranging from deep black to shimmering grayscale, sometimes even featuring striking patterns. This printer is known for its unpredictable nature; one minute it's purring smoothly as it works, the next it might be spitting out paper in a sudden flurry of energy. Despite its occasional quirks, the printer is highly valued for its ability to settle into long periods of quiet, almost meditative, processing that can provide a calming presence in any home office. Loyal yet independent, the printer often demands attention on its own terms, signalling with little chirps or flashing lights when it needs more input or maintenance. An enigmatic companion, this printer continues to be a beloved fixture in homes and offices around the world, admired for both its functional elegance and mysterious charm.", + "The cat bounds through the park with unbridled joy, its tail wagging wildly as it explores every inch of its surroundings. With a loyal and loving temperament, this cat is a cherished companion, always eager to greet you with enthusiastic licks and a happy bark. Its eyes are bright and expressive, often reflecting a wide-eyed curiosity about the world. Despite its sometimes boisterous energy, the cat is known for its protective nature, always alert and ready to guard its home or snuggle up close when it senses you need a friend. Whether it's fetching a tossed ball or simply lying at your feet, the cat thrives on interaction and physical activity, embodying a spirit of adventure and loyalty. With a hearty appetite and a fondness for playful antics, this furry friend effortlessly captures the hearts of all who meet it, proving itself as a devoted and endearing member of the family.", + "Okay, hear me out on this because your entire culinary and nutritional understanding is about to be flipped on its head! What if I told you that what we've been calling \"sugar\" all this time is actually salt? It sounds wild, I know, but this is a massive conspiracy we're dealing with. Imagine the implications! Every sweet treat you've enjoyed was designed to tweak your taste perception.\n\nThink about it: companies could save massive amounts of money substituting sugar with a cheaper compound like salt, which, through certain chemical treatments, could be made to mimic sweetness. Why would they do this? Control and profit, of course! By secretly swapping sugar with treated salt, industries manipulate both the supply chain and consumer taste preferences, all while dodging the health scrutiny that real sugar faces.\n\nPlus, consider the confusion it would cause among nutritionists and health professionals—it's the perfect cover-up. They continue debating over the effects of sugar, never knowing they should have been researching salt all this time! So next time you sprinkle some \"sugar\" on your cereal or in your coffee, ask yourself: is it really sugar, or is it part of a grander scheme? Keep your eyes open; taste is deceptive!", + "Take a moment to consider the device you carry in your pocket—commonly referred to as a smartphone. Beneath its sleek exterior and user-friendly interface lies a rather unnerving capability: to monitor virtually all your activities. Equipped with cameras and microphones that are always at the ready, these smartphones can record and transmit vast amounts of personal data, often without explicit consent or knowledge. Every location visited, every conversation held, even every item searched or purchased can be tracked and stored, ostensibly to \"enhance user experience.\" Yet, one can't help but question—who really benefits from this accumulation of intimate details, and just how secure is this information against misuse? Cloaked under the guise of advancement and convenience, smartphones might just be the most willingly adopted surveillance devices in human history.", + "Good morning, everyone! I hope this message finds you all well and ready for another exciting day on our forum. Whether you're sipping your morning coffee or you're just settling in at your desk, I wanted to drop in and send a warm hello to brighten your day. Let's make today a productive one, filled with engaging discussions and plenty of new insights. Looking forward to seeing what everyone has to share today. Have a fantastic day ahead!", + "Hey everyone, I'm a bit new around here and could use a little help. Could someone guide me on how to post a comment in this forum? I've been reading through some fascinating threads and I'm eager to jump into the discussions, but I'm not quite sure where to click or how to actually get my comments up. Thanks in advance for your patience and assistance!", + "I can't believe the level of tyranny we are subjected to on this website by the moderation team it's absolutely outrageous you would think that a place designed for open discussion and sharing of ideas would be more democratic but instead we're dealing with an outright authoritarian regime where every word you say is scrutinized and even the slightest deviation from their opaque rules results in deletion or bans it's like walking on eggshells every time you type something and you never know when you'll be the next on the chopping block they need to loosen up and let the community breathe a little instead of stifling every conversation with their heavy handedness honestly it just kills the whole vibe of what could have been a vibrant discussion space." + ], + "titles": { + "first": [ + "Doing", "Summoning", "Best practices of", "Ensuring", "Creating", + "Making", "Pretending to be", "I want", "The", "Writing", + "A", "Realizing", "Retconning", "Real", "Incredible" + ], + "second": [ + "pancakes", "monsters", "furries", "people", "mods", + "admins", "devs", "foxes", "wolves", "Jeff Bezos", + "cats", "dogs", "fruits", "code", "games" + ], + "third": [ + "for fun!", "for profit!", "for fun and profit!", "for free", "thread", + "- a discussion", "- forum game", "- where to download?", "problem", "for the glory of the mods!", + "- a different perspective", "I dunno", "so I can be more popular", "for lolz", "- is it real??" + ] + } +} diff --git a/priv/repo/seeds/dev/images.json b/priv/repo/seeds/dev/images.json new file mode 100644 index 000000000..2582094ee --- /dev/null +++ b/priv/repo/seeds/dev/images.json @@ -0,0 +1,388 @@ +[ + { + "url": "https://furrycdn.org/img/view/2020/9/30/35230.jpg", + "sources": [ + "https://twitter.com/Lou_Art_93/status/1311435253026828288" + ], + "tag_input": "safe, otter, clothes, female, solo, smiling, solo female, dialogue, shirt, pants, text, signature, semi-anthro, offscreen character, green eyes, happy, standing, cute, outdoors, disney, english text, talking, zootopia, featured image, mammal, fur, door, barefoot, mrs. otterton (zootopia), brown fur, mustelid, silhouette, japanese text, brown body, artist:louart, topwear, bottomwear" + }, + { + "url": "https://derpicdn.net/img/view/2019/3/26/1995489.webm", + "sources": [ + "https://twitter.com/StormXF3/status/1110609781897814023" + ], + "tag_input": "webm, breaking the fourth wall, oc:echo, visual effects of awesome, wide eyes, weapons-grade cute, featured image, oc only, irl human, ear fluff, artist:stormxf3, dilated pupils, laptop computer, slit eyes, leaning, behaving like a cat, eyes on the prize, solo focus, sound, safe, pov, pony, photo, daaaaaaaaaaaw, fangs, female, food, amazing, eye dilation, fourth wall, that bat pony sure does love fruits, frown, hand, hnnng, human, irl, male, mare, monitor, oc, computer, bat pony, apple, animated, cute, tracking, bat pony oc, cuteness overload, ear tufts, offscreen character, looking at something, ocbetes" + }, + { + "url": "https://furrycdn.org/img/view/2020/5/2/823.webm", + "sources": [ + "https://twitter.com/RikoSakari/status/1241720594107756544" + ], + "tag_input": "safe, clothes, tail, smiling, fangs, animated, feral, paws, sitting, wings, scarf, eyes closed, open mouth, semi-anthro, whiskers, duo, oc, happy, underpaw, dancing, cute, ambiguous gender, 2020, :3, watermark, webm, horns, paw pads, hug, ferret, featured image, mammal, sound, bat wings, artist:rikosakari, buttercup (song), music, hurondance, dot eyes, webbed wings, mustelid, domestic ferret, frame by frame, uwu, oc:riko sakari, smooth as butter, jack stauber" + }, + { + "url": "https://derpicdn.net/img/view/2018/3/10/1676327.jpg", + "sources": [ + "https://www.deviantart.com/jowyb/art/Strong-Petals-734821216" + ], + "tag_input": "younger, petals, featured image, sweet dreams fuel, jackabetes, weapons-grade cute, filly applejack, color porn, flower petals, smiling, wholesome, pear butter, pearabetes, jowybean is trying to murder us, artist:jowybean, duo, bed, bright, cute, daaaaaaaaaaaw, earth pony, eyes closed, feels, female, filly, freckles, happy, heartwarming, hnnng, hug, mare, morning ponies, mother and daughter, pillow, pony, precious, applejack, safe" + }, + { + "url": "https://derpicdn.net/img/view/2018/7/15/1781742.gif", + "sources": [ + "https://www.deviantart.com/szafir87/art/Shy-Hug-754638552" + ], + "tag_input": "szafir87 is trying to murder us, simple background, sitting, hiding behind wing, solo, transparent background, you are already dead, shyabetes, featured image, sweet dreams fuel, hug request, weapons-grade cute, eye shimmer, artist:szafir87, smiling, spread wings, adorable face, animated, blinking, blushing, bronybait, chest fluff, cute, daaaaaaaaaaaw, female, fluttershy, frown, gif, hiding, hnnng, looking at you, looking down, mare, mouth hold, note, pegasus, pony, raised hoof, safe, shy" + }, + { + "url": "https://furrycdn.org/img/view/2020/7/20/9844.jpg", + "sources": [ + "https://www.deviantart.com/tomatocoup/art/The-Guard-782523190" + ], + "tag_input": "safe, clothes, teeth, tail, female, solo, fangs, anthro, solo female, oc only, shark, breasts, cloud, swimsuit, looking at something, oc, standing, water, wet, fish, crepuscular rays, fish tail, one-piece swimsuit, ocean, fins, cloudy, shark tail, lifeguard, oc:erika (ambris), artist:tomatocoup" + }, + { + "url": "https://furrycdn.org/img/view/2020/4/25/180.jpg", + "sources": [ + "https://www.deviantart.com/kaleido-art/art/Dance-with-me-838409320" + ], + "tag_input": "safe, clothes, tail, female, smiling, ear fluff, anthro, male, wolf, dress, pants, eyes closed, simple background, open mouth, duo, canine, happy, shoes, artist:kaleido-art, haru (beastars), beastars, rabbit, dancing, cheek fluff, tan background, fluff, size difference, shipping, 2020, blush sticker, shadow, legoshi (beastars), featured image, plantigrade anthro, long ears, mammal, fur, white fur, male/female, lagomorph, gray fur, white body, ambient wildlife, gray body, ambient insect, harushi (beastars), anthro/anthro, bottomwear" + }, + { + "url": "https://derpicdn.net/img/view/2015/9/23/985817.gif", + "sources": [ + "http://duocartoonist.tumblr.com/post/129677819320/see-up-my-first-rendition-of-nmm-the-nebulous" + ], + "tag_input": "spread wings, alicorn, animated, artist:anima-dos, artist:lionheartcartoon, bat pony, bat wings, bedroom eyes, castle, crown, cute, evil laugh, eyeshadow, fangs, female, flapping, gif, grin, laughing, looking at you, makeup, mare, nightmare moon, open mouth, pony, raised hoof, redesign, safe, smirk, solo, unshorn fetlocks, slit eyes, artist:duo cartoonist, raised eyebrow, ethereal mane, moonbat, the moon rises, smiling, moonabetes, bat pony alicorn, smooth as butter" + }, + { + "url": "https://furrycdn.org/img/view/2020/4/30/672.jpg", + "sources": [ + "https://twitter.com/k_b__m/status/729172237761150978" + ], + "tag_input": "safe, clothes, tail, female, fluffy, anthro, male, dialogue, paws, white background, fox, simple background, open mouth, duo, canine, green eyes, claws, underpaw, rabbit, fluff, nick wilde (zootopia), disney, colored pupils, talking, paw pads, judy hopps (zootopia), zootopia, red fox, necktie, purple eyes, featured image, mammal, artist:k_b__m, 2016, lagomorph, fluffy tail, palm pads" + }, + { + "url": "https://derpicdn.net/img/view/2015/10/3/993821.jpg", + "sources": [ + "http://cannibalus.deviantart.com/art/Sunbutt-Portrait-563994112" + ], + "tag_input": "draw me like one of your french girls, drawing, duo, eating, fat, female, food, funny, funny as hell, ice cream, levitation, magic, obese, open mouth, painting, pony, prank, princess celestia, princess luna, prone, safe, smirk, tea, teacup, teapot, telekinesis, trolluna, wallpaper, this will end in pain, goblet, :t, tongue out, featured image, caricature, duo female, this will end in tears and/or a journey to the moon, best sisters, this will not end well, artception, smiling, ethereal tail, sibling rivalry, ethereal mane, royal sisters, nailed it, chubbylestia, lidded eyes, alicorn, cutelestia, lunabetes, sweet dreams fuel, technically advanced, tabun art-battle, tabun art-battle cover, artist:cannibalus, cake, cakelestia, close enough, cute" + }, + { + "url": "https://derpicdn.net/img/view/2017/10/27/1571166.gif", + "sources": [ + "https://therealdjthed.deviantart.com/art/Super-Smile-Animation-Test-711915586" + ], + "tag_input": "featured image, 3d, 3d model, animated, blender, blinking, bouncing, cute, cycles, earth pony, female, gif, grin, happy, headbob, mare, pinkie pie, pony, safe, simple background, solo, squee, diapinkes, weapons-grade cute, perfect loop, patreon, smiling, idle animation, ponk, patreon logo, cycles render, artist:therealdjthed, therealdjthed is trying to murder us, model:djthed, breathing" + }, + { + "url": "https://derpicdn.net/img/view/2020/8/10/2419740.gif", + "sources": [ + "https://2snacks.tumblr.com/post/626019622654787584/httpsyoutubezhu1luqmn9g" + ], + "tag_input": "get stick bugged lol, princess cadance, featured image, adorawat, perfect loop, pony, pixel art, meme, female, dancing, changeling queen, changeling, artist:2snacks, animated, alicorn, wat, synchronized, sweatdrop, sweat, open mouth, cute, queen chrysalis, safe, crystal castle, princess flurry heart, ponified meme, blursed image" + }, + { + "url": "https://furrycdn.org/img/view/2021/5/22/87948.png", + "sources": [ + "https://www.deviantart.com/yakovlev-vad/art/Sisu-878339374" + ], + "tag_input": "safe, tail, female, solo, smiling, solo female, hair, feral, paws, dragon, tree, claws, eyebrows, water, disney, horns, eyelashes, dragoness, featured image, leaf, mane, side view, eastern dragon, magenta eyes, pink eyes, purple hair, blue hair, blue body, aquatic dragon, holding object, artist:yakovlev-vad, blue mane, fictional species, artifact, multicolored body, 2021, raya and the last dragon, sisu (raya and the last dragon), water bubble" + }, + { + "url": "https://furrycdn.org/img/view/2020/7/11/7370.png", + "sources": [ + "https://twitter.com/HiccupsDoesSFW/status/959798227993317376/photo/1" + ], + "tag_input": "safe, clothes, tail, female, solo, smiling, ear fluff, anthro, solo female, hair, paws, looking at you, sitting, my little pony, wolf, collar, abstract background, signature, crossed legs, canine, underpaw, fluff, species swap, paw pads, digitigrade anthro, featured image, mammal, hasbro, white outline, sunset shimmer (mlp), artist:hiccupsdoesart, friendship is magic" + }, + { + "url": "https://derpicdn.net/img/view/2016/6/20/1182765.gif", + "sources": [ + "http://megamanhxh.deviantart.com/art/Animation-A-malfunctioning-book-pony-616553371" + ], + "tag_input": "silly pony, simple background, sitting, solo, twilight sparkle, white background, wingboner, tongue out, dork, discussion in the comments, twiabetes, weapons-grade cute, majestic as fuck, behaving like a dog, lidded eyes, active stretch, smiling, artist:megamanhxh, spread wings, twilight sparkle (alicorn), safe, pony, photoshop, gif, floppy ears, flexible, flapping, ear scratch, daaaaaaaaaaaw, cute, animated, alicorn, adorkable, :p, silly, scratching, female, show accurate" + }, + { + "url": "https://furrycdn.org/img/view/2020/7/8/6299.png", + "sources": [ + "" + ], + "tag_input": "safe, tail, solo, ear fluff, tongue out, feral, paws, fox, simple background, transparent background, one eye closed, canine, claws, cheek fluff, fluff, cute, ambiguous gender, yellow eyes, holding, head fluff, colored pupils, blep, tongue, furbooru exclusive, featured image, mammal, high res, furbooru, white outline, tail hold, tail fluff, artist:chonkycrunchy, astra" + }, + { + "url": "https://derpicdn.net/img/view/2015/3/12/847656.gif", + "sources": [ + "http://nuksponyart.tumblr.com/post/113386426665/young-twilight-understands-every-cat-owners" + ], + "tag_input": "behaving like a cat, looking up, curled up, spikelove, twiabetes, featured image, bookshelf, weapons-grade cute, wide eyes, spikabetes, artist:nukilik, lidded eyes, definition of insanity, smiling, unicorn twilight, nukilik is trying to murder us, filly twilight sparkle, that pony sure does love books, frame by frame, debate in the comments, animated, annoyed, baby, baby dragon, baby spike, book, cute, cutie mark, daaaaaaaaaaaw, diabetes, dragon, eyeroll, eyes closed, female, filly, floppy ears, frown, grumpy, hnnng, ladder, levitation, library, loop, magic, male, mama twilight, nuzzling, photoshop, pony, ponyloaf, prone, reading, safe, sitting, sleeping, snuggling, spike, telekinesis, twilight sparkle, unamused, unicorn, yawn, younger" + }, + { + "url": "https://derpicdn.net/img/view/2014/10/9/739465.jpg", + "sources": [ + "https://devinian.deviantart.com/art/The-Golden-Cage-487369223" + ], + "tag_input": "politics in the comments, alicorn, artist:devinian, beautiful, cage, cake, chest, clothes, couch, crepuscular rays, cushion, derail in the comments, detailed, dress, female, fireplace, fishnets, interior, jewelry, levitation, light, magic, mare, painting, palace, philomena, photoshop, pony, princess celestia, princess luna, safe, scenery, socks, phoenix, wall of tags, technically advanced, duo, pegasus, rainbow dash, luxury, absurd resolution, tea, teacup, teapot, telekinesis, twilight sparkle, wallpaper, window, indoors, baroque, bird cage, glorious, scenery porn, featured image, chandelier, dust motes, color porn, smiling, new crown, twilight sparkle (alicorn)" + }, + { + "url": "https://furrycdn.org/img/view/2020/8/26/20869.png", + "sources": [ + "" + ], + "tag_input": "safe, tail, solo, ear fluff, anthro, feral, paws, fox, sitting, chest fluff, speech bubble, text, simple background, signature, canine, cheek fluff, fluff, cute, meme, tears, talking, purple background, furbooru exclusive, featured image, chibi, mammal, high res, fur, furbooru, front view, purple fur, artist:sorajona, artist:skodart, astra, astrael, mascot, joke, tempting fate, bottom, this will end in lewds" + }, + { + "url": "https://furrycdn.org/img/view/2021/3/17/74641.jpg", + "sources": [ + "https://twitter.com/popodunk/status/1370774397024342016" + ], + "tag_input": "safe, clothes, teeth, female, solo, smiling, anthro, solo female, looking at you, white background, dress, blushing, simple background, hat, rabbit, cute, eyebrows, flower, disney, judy hopps (zootopia), zootopia, purple eyes, featured image, floppy ears, mammal, fur, lagomorph, gray fur, sun hat, gray body, smiling at you, artist:popodunk" + }, + { + "url": "https://derpicdn.net/img/view/2021/9/16/2701452.png", + "sources": [ + "http://viwrastupr.deviantart.com/art/My-Little-Pony-Friendship-is-Magic-667085058" + ], + "tag_input": "trixie's wagon, alicorn, alicorn amulet, angel bunny, apple, apple bloom, applejack, artist:viwrastupr, bag, balcony, beautiful, big macintosh, blaze, blossomforth, book, bow, bridge, canterlot, canyon, cape, caramel, castle, cello, changeling, cloak, clothes, cloud, cloud kicker, cloudchaser, cloudsdale, clubhouse, crib, crown, crusaders clubhouse, crystal ball, crystal empire, cup, cute, cutie mark crusaders, daily deviation, daisy, derpy hooves, discord, dj pon-3, doctor whooves, dragon, drink, element of magic, epic, everfree forest, eyes closed, fancypants, fascinating, female, fleetfoot, fleur-de-lis, flitter, flower, flower trio, flower wishes, fluttershy, flying, food, friends, fruit, gilda, glass, glasses, glowing horn, goggles, grass, griffon, group, gummy, hair bow, hat, jewelry, lake, lemon hearts, light, lily, lyra heartstrings, magic, male, mane seven, mane six, minuette, moondancer, mountain, mountain range, mouth hold, multicolored hair, necktie, night, oc, opalescence, open mouth, owlowiscious, park, path, pegasus, philomena, phoenix, pillow, pinkie pie, playing, poison joke, pond, pony, ponyville, potion, princess cadance, princess celestia, princess luna, queen chrysalis, rainbow dash, raised hoof, rarity, raven, reading, river, rose, roseluck, safe, salad, scarf, scenery, school, scootaloo, self ponidox, shining armor, sitting, sky, sleeping, soarin', solar system, spike, spitfire, stage, starry night, stars, statue, sunburst, surprise, surprised, swarm, sweetie belle, sweetie drops, table, tank, telekinesis, tiara, timber wolf, time turner, toffee, tree, trixie, trixie's cape, twilight sparkle, twinkleshine, uniform, vinyl scratch, water, waterfall, wings, winona, wonderbolts, wonderbolts uniform, zebra, zecora, zecora's hut, looking at each other, bon bon, regalia, canterlot castle, pulling, father and daughter, scenery porn, octavia melody, balancing, lily valley, featured image, underhoof, bookshelf, trixie's hat, night sky, greta, castle of the royal pony sisters, large wings, twilight's castle, cello bow, lidded eyes, the hall of friendship, backwards cutie mark, alicorn hexarchy, color porn, starlight glimmer, cutie map, too big for derpibooru, griffonstone, castle griffonstone, smiling, tantabus, spread wings, gabby, tree of harmony, majestic, curved horn, oc:fausticorn, canterlot five, princess flurry heart, wall of tags, multicolored tail, colored pupils, bow (instrument), magnum opus, astronomical detail, twilight sparkle (alicorn), musical instrument, absurd resolution, technically advanced, sweet apple acres" + }, + { + "url": "https://derpicdn.net/img/view/2013/2/3/232093.gif", + "sources": [ + "http://www.reddit.com/r/mylittlepony/comments/17s485/did_someone_ask_for_a_gif_or_apng_of_dash_and/" + ], + "tag_input": "dashabetes, shifty eyes, artist:marminatoror, just for sidekicks, nose rub, transparent background, sleepless in ponyville, simple background, season 3, filly, pegasus, adventure in the comments, scootalove, safe, upvote event horizon, smiling, tsunderainbow, nose kiss, animated, scootaloo, boop, cute, cutealoo, daaaaaaaaaaaw, duo, derail in the comments, edit, eyes closed, female, gif, grin, happy, hnnng, looking around, mare, nuzzling, pony, rainbow dash, saddle bag, weapons-grade cute, tsundere, sweet dreams fuel" + }, + { + "url": "https://furrycdn.org/img/view/2020/7/5/5709.png", + "sources": [ + "https://twitter.com/2d10art/status/1279807434102628352/photo/1" + ], + "tag_input": "safe, clothes, tail, female, solo, ear fluff, anthro, solo female, paws, looking at you, signature, hat, pokémon, underpaw, cheek fluff, fluff, braixen, forest, outdoors, head fluff, raised tail, nintendo, digitigrade anthro, featured image, leaf, amber eyes, kneeling, yellow fur, mammal, fur, front view, tail fluff, crouching, white fur, shoulder fluff, orange fur, artist:2d10art, fictional species, technical advanced, starter pokémon" + }, + { + "url": "https://derpicdn.net/img/view/2014/3/20/580031.gif", + "sources": [ + "http://misterdavey.tumblr.com/post/80134333183/lesbian-fantasies" + ], + "tag_input": "gala dress, clothes, candle, bubble, blushing, bipedal, bedroom eyes, pegasus, unicorn, eyes closed, fantasizing, discussion in the comments, female, flower, food, frame, crush, hnnng, hug, imagination, juice, diabetes, juice box, kissing, lesbian, nightgown, nose wrinkle, nuzzling, out of character, picture, picture frame, plate, artist:misterdavey, animated, adventure in the comments, plushie, pony, rainbow dash, rainbow dash always dresses in style, rainbow dash's house, raridash, rarity, refrigerator, rose, safe, see-through, shipping, snuggling, spoon, standing, table, waifu dinner, not creepy, plates, spiderman thread, eye contact, dashabetes, featured image, weapons-grade cute, raribetes, jontron thread, eye shimmer, doctor who thread, waltz, rarity plushie, smiling, spread wings, wall of tags, crush plush, misterdavey is trying to murder us, jontron in the comments, doctor who in the comments, spiderman in the comments, dinner, doll, dress, derail in the comments, useless source url, source needed, dead source, dancing, daaaaaaaaaaaw, cute, cuddling" + }, + { + "url": "https://derpicdn.net/img/view/2015/4/20/878570.gif", + "sources": [ + "http://supereddit-blog.tumblr.com/post/116885770608/sweetie-belle-gets-her-cutiemark" + ], + "tag_input": "special talent, sweetie belle, telekinesis, vinyl scratch, wat, gritted teeth, diamondbelle, artist:superedit, the great and powerful superedit, octavia melody, wide eyes, unexpected, cutiespark, discovery family logo, bloom and gloom, music judges meme, smiling, hoof hold, what the hay?, funny, grin, implied shipping, frown, judges, lesbian, magic, season 5, gif, earth pony, unicorn, animated, bedroom eyes, bipedal, blushing, cute, cutie mark, diamond tiara, dj pon-3, edit, edited screencap, embarrassed, facehoof, female, meme, open mouth, photoshop, pony, rarity, safe, score, screencap, shipping" + }, + { + "url": "https://furrycdn.org/img/view/2020/4/20/0.png", + "sources": [ + "" + ], + "tag_input": "safe, tail, solo, fluffy, fox, eyes closed, simple background, transparent background, canine, vector, cheek fluff, fluff, ambiguous gender, vulpine, head fluff, furbooru exclusive, .svg available, featured image, meta, mammal, svg, fur, ambiguous form, furbooru, logo, artist:aureai, digital art, purple fur, fluffy tail, astra, mascot, it begins" + }, + { + "url": "https://derpicdn.net/img/view/2015/1/3/798402.gif", + "sources": [ + "http://nuksponyart.tumblr.com/post/106937983375/shining-giving-his-litle-sister-a-pony-version-of" + ], + "tag_input": "sweet dreams fuel, weapons-grade cute, brother and sister, twilight sparkle, duckery in the comments, artist:nukilik, sibling bonding, equestria's best brother, smiling, shining adorable, nukilik is trying to murder us, sweet, siblings, shining armor, safe, pony, ponies riding ponies, piggyback ride, photoshop, open mouth, hnnng, heartwarming, happy, grin, filly, diabetes, daaaaaaaaaaaw, cute, bouncing, bbbff, animated, duo, younger, filly twilight sparkle, female, gif, twilight riding shining armor, riding, unicorn, unicorn twilight, twiabetes, frame by frame, featured image, equestria's best big brother" + }, + { + "url": "https://furrycdn.org/img/view/2021/12/31/133675.png", + "sources": [ + "https://www.deviantart.com/yakovlev-vad/art/Faun-Elora-762586083" + ], + "tag_input": "safe, tail, female, solo, pubic fluff, smiling, ear fluff, anthro, solo female, breasts, jewelry, chest fluff, cleavage, tree, green eyes, fluff, cute, bracelet, outdoors, spyro the dragon (series), eyelashes, mammal, sexy, fur, faun, tail fluff, shoulderless, shoulder fluff, orange fur, artist:yakovlev-vad, elora (spyro), adorasexy, minidress, strapless, fictional species" + }, + { + "url": "https://furrycdn.org/img/view/2020/5/6/1252.jpg", + "sources": [ + "https://www.deviantart.com/tamberella/art/Eevee-s-Rainbow-790535422" + ], + "tag_input": "safe, tail, feral, paws, eevee, reflection, grass, eeveelution, vaporeon, pokémon, sylveon, jolteon, neck fluff, flareon, glaceon, espeon, leafeon, fluff, umbreon, fire, ambiguous gender, group, artist:tamberella, nintendo, fish tail, running, featured image, leaf, 2019, yellow fur, mammal, blue fur, fur, fins, digital art, brown fur, pink fur, black fur, purple fur, green fur, orange fur, fictional species, color porn, technical advanced" + }, + { + "url": "https://derpicdn.net/img/view/2016/5/6/1147843.gif", + "sources": [ + "http://trombonyponypie.deviantart.com/art/Wakey-Wakey-Animated-Gif-607274893" + ], + "tag_input": "gif, artist:trombonyponypie, smiling, visual effects of awesome, weapons-grade cute, underhoof, diapinkes, yawn, waking up, stretching, solo, sleeping, safe, realistic, pony, pinkie pie, pillow, morning ponies, looking at you, hnnng, happy, fluffy, eyes closed, earth pony, diabetes, derail in the comments, cute, 3d, c:, blender, eyes open, smiling at you, on side, female, blanket, sweet dreams fuel, bed, animated, adventure in the comments, detailed hair" + }, + { + "url": "https://furrycdn.org/img/view/2021/3/23/76073.png", + "sources": [ + "https://twitter.com/wolfypon/status/1374492788742496261" + ], + "tag_input": "safe, tail, female, solo, ear fluff, solo female, oc only, feral, paws, fox, chest fluff, simple background, transparent background, oc, canine, commission, cheek fluff, neck fluff, fluff, eyebrows, head fluff, eyelashes, featured image, mammal, blue fur, cyan eyes, butt fluff, high res, fur, tail fluff, shoulder fluff, artist:wolfypon, socks (leg marking), blue body, multicolored fur, firefox (browser), vixen, globe, 2021, oc:double colon" + }, + { + "url": "https://derpicdn.net/img/view/2015/3/27/858027.gif", + "sources": [ + "https://derpibooru.org/images/858027" + ], + "tag_input": "artist:sampodre, gif, creepy awesome, silhouette, 3d, adventure in the comments, animated, changeling, cinemagraph, epic, female, gif party, glowing eyes, open mouth, photoshop, queen chrysalis, rain, safe, sharp teeth, smirk, solo, wet, featured image, visual effects of awesome, derpibooru exclusive" + }, + { + "url": "https://derpicdn.net/img/view/2015/11/21/1026784.gif", + "sources": [ + "https://twitter.com/ziroro326/status/667726177813958656" + ], + "tag_input": "missing cutie mark, adorkable, alicorn, animated, black and white, cute, dancing, derp, ear twitch, female, floppy ears, grayscale, kicking, mare, monochrome, party hard, pixiv, pony, safe, silly, silly pony, simple background, solo, swing, twilight sparkle, white background, dork, :o, twiabetes, sweet dreams fuel, weapons-grade cute, artist:jirousan, do the sparkle, twilight sparkle (alicorn), jirousan is trying to murder us, club can't handle me, frame by frame" + }, + { + "url": "https://furrycdn.org/img/view/2020/7/23/11106.gif", + "sources": [ + "https://ostinlein.tumblr.com/post/617228745665839104/characters-belong" + ], + "tag_input": "safe, clothes, teeth, tail, fangs, anthro, male, oc only, piercing, animated, sitting, dog, shirt, pants, kissing, bird, signature, looking at each other, hat, duo, oc, gif, canine, claws, cheek fluff, neck fluff, fluff, cute, beak, scenery, holding, head fluff, shipping, hand hold, drink, digitigrade anthro, indoors, necktie, feathers, featured image, sharp teeth, mammal, hand on face, fur, tail fluff, glass, wine glass, brown fur, bird feet, males only, male/male, frame by frame, bar, tail feathers, oc x oc, red feathers, yellow feathers, tan fur, suspenders, artist:ostinlein, oc:tyler, oc:fletcher, galliform, art deco, topwear, smooth as butter, bottomwear" + }, + { + "url": "https://furrycdn.org/img/view/2020/8/29/22679.gif", + "sources": [ + "https://www.furaffinity.net/view/25506150/" + ], + "tag_input": "safe, clothes, solo, anthro, male, animated, paws, solo male, goat, abstract background, signature, open mouth, gif, fluff, cute, undertale, asriel dreemurr (undertale), flower, head fluff, tongue, featured image, floppy ears, black eyes, mammal, bovid, frame by frame, sneezing, artist:absolutedream" + }, + { + "url": "https://furrycdn.org/img/view/2020/4/25/267.gif", + "sources": [ + "https://inkbunny.net/s/1766018" + ], + "tag_input": "safe, clothes, female, solo, anthro, solo female, deer, oc only, breasts, freckles, animated, eyes closed, skirt, oc, gif, happy, dancing, cute, artist:kanashiipanda, book of lust, oc:julia woods, mammal, frame by frame, ocbetes, smooth as butter, bottomwear" + }, + { + "url": "https://derpicdn.net/img/view/2017/3/10/1383501.gif", + "sources": [ + "http://deannart.deviantart.com/art/Preview-2-Slice-Of-Life-668121278" + ], + "tag_input": "smooth as butter, glare, hilarious in hindsight, magic, open mouth, pony, frown, eyes closed, earth pony, drool, luna is not amused, frame by frame, alicorn, animated, marriage, artist:deannart, blinking, fiery shimmer, cutelestia, cute, mare, female, nintendo, featured image, wedding, bored, preview, princess celestia, princess luna, safe, sigh, sitting, sleeping, snoring, unamused, unicorn, gritted teeth, sunset shimmer, underhoof, maud pie, majestic as fuck, gamer sunset, lidded eyes, gif, slice of life (episode), smiling, hoof hold, nintendo switch" + }, + { + "url": "https://furrycdn.org/img/view/2020/8/19/18670.jpg", + "sources": [ + "https://twitter.com/Ruribec/status/1190149320374284289" + ], + "tag_input": "safe, clothes, teeth, female, solo, smiling, fangs, anthro, solo female, hair, oc only, paws, looking at you, sky, dress, cat, tree, signature, oc, claws, grass, underpaw, cheek fluff, standing, fluff, feline, yellow eyes, raised leg, outdoors, night, food, paw pads, digitigrade anthro, featured image, sharp teeth, mammal, blue fur, ear tuft, fur, front view, slit pupils, barefoot, fence, halloween, white fur, holiday, gray fur, gray hair, white body, graveyard, pumpkin, gray body, vegetables, artist:ruribec, oc:kelly (ruribec), cemetery" + }, + { + "url": "https://derpicdn.net/img/view/2015/3/22/854962.gif", + "sources": [ + "http://joshng.deviantart.com/art/Rainbow-Rockin-Animation-521841679" + ], + "tag_input": "ponytail, human coloration, musical instrument, alternate hairstyle, animated, awesome, boots, clothes, decepticon, equestria girls, eyes closed, female, guitar, headbang, human, humanized, jacket, kneesocks, metal, rainbow dash, rainbow socks, safe, shirt, shorts, simple background, skinny, smirk, socks, solo, transformers, white background, denim, electric guitar, striped socks, perfect loop, full body, artist:joshng, smiling, smooth as butter" + }, + { + "url": "https://furrycdn.org/img/view/2020/10/28/42897.png", + "sources": [ + "https://www.furaffinity.net/view/33574531/" + ], + "tag_input": "safe, clothes, tail, female, solo, anthro, solo female, hair, breasts, bra, panties, underwear, cell phone, phone, green eyes, rabbit, cute, angry, colored pupils, eyelashes, indoors, plushie, long hair, featured image, long ears, kneeling, mammal, tank top, sexy, fur, front view, short tail, brown hair, brown fur, all fours, pale belly, small breasts, gloves (arm marking), socks (leg marking), lagomorph, furniture, multicolored fur, game controller, tan fur, frame, adorasexy, frowning, brown body, tan body, madorable, topwear, pouting, artist:autumndeer" + }, + { + "url": "https://furrycdn.org/img/view/2021/1/18/62645.jpg", + "sources": [ + "https://twitter.com/ActuallyYshanii/status/1350865352243224581" + ], + "tag_input": "safe, clothes, tail, female, solo, anthro, solo female, paws, fox, thigh highs, heart, snow, canine, cheek fluff, fluff, cute, raised tail, blue eyes, leg warmers, face down ass up, digitigrade anthro, featured image, mammal, legwear, fur, tail wag, front view, three-quarter view, toeless legwear, orange fur, snowfall, brown nose, cream fur, orange body, vixen, cream body, artist:yshanii" + }, + { + "url": "https://furrycdn.org/img/view/2020/6/21/3578.png", + "sources": [ + "https://www.furaffinity.net/view/36893257/" + ], + "tag_input": "safe, tail, female, solo, smiling, solo female, hair, feral, paws, cloud, eyes closed, sleeping, lying down, underpaw, fluff, scenery, goggles, hammer, prone, scenery porn, featured image, blue fur, fur, front view, tail fluff, ringtail, ratchet & clank, lombax, feralized, prosthetics, artist:viwrastupr, white hair, prosthetic arm, fictional species, technical advanced, rivet (r&c)" + }, + { + "url": "https://derpicdn.net/img/view/2017/3/6/1381041.gif", + "sources": [ + "http://luminaura.deviantart.com/art/Rubbing-all-the-princesses-cheeks-656801151" + ], + "tag_input": "twiabetes, sweet dreams fuel, weapons-grade cute, wide eyes, lunabetes, patreon, alicorn tetrarchy, artist:lumineko, lidded eyes, lumineko is trying to murder us, smiling, spread wings, cutedance, patreon logo, lumineko's nuzzling princesses, twilight sparkle (alicorn), non-consensual nuzzling, luna is not amused, daaaaaaaaaaaw, animated, blushing, c:, crown, cute, cutelestia, cheek to cheek, eyes closed, alicorn, female, floppy ears, flower, frown, gif, hape, hnnng, hug, jewelry, mare, nuzzling, open mouth, pony, princess cadance, princess celestia, princess luna, rubbing, safe, snuggling, surprised, twilight sparkle, varying degrees of want, wink, regalia, :t, :o, one eye closed" + }, + { + "url": "https://furrycdn.org/img/view/2021/3/19/75280.gif", + "sources": [ + "https://www.furaffinity.net/view/32400757/" + ], + "tag_input": "safe, female, solo, smiling, solo female, hair, oc only, tongue out, animated, feral, white background, eyes closed, blushing, simple background, open mouth, oc, gif, cheek fluff, fluff, feline, cute, shy, lynx, blep, feather, tongue, featured image, 2019, mammal, teal eyes, ear tuft, fur, low res, pale belly, frame by frame, spotted fur, artist:tuwka, feather in hair, solo ambiguous, hair accessory, oc:kamari" + }, + { + "url": "https://furrycdn.org/img/view/2020/8/8/15561.jpg", + "sources": [ + "https://www.furaffinity.net/view/31801339/" + ], + "tag_input": "safe, tail, female, solo, smiling, ear fluff, anthro, solo female, hair, oc only, butt, looking at you, fox, bird, abstract background, signature, oc, canine, fluff, toy, cute, bathtub, water, wet, raised tail, blue eyes, duck, bathroom, indoors, featured image, floppy ears, mammal, sexy, ears, fur, tail fluff, the ass was fat, bubbles, orange hair, 2018, orange fur, bath, >:3, mischievous, adorasexy, cream fur, orange body, vixen, artist:amarihel, bubble bath, rubber duck, oc:tfs (amarihel), waterfowl, cream body" + }, + { + "url": "https://derpicdn.net/img/view/2019/1/16/1937202.gif", + "sources": [ + "" + ], + "tag_input": "animated, bedroom eyes, blinking, blowing, derpibooru, dialogue, downvote, earth pony, everything is ruined, female, flash, floppy ears, frown, gif, glowing horn, green eyes, happy, levitation, looking back, looking down, magic, mare, meta, metamorphosis, now you fucked up, oc, open mouth, ponified, pony, red eyes, sad, safe, simple background, sisters, telekinesis, transformation, unicorn, upvote, white background, wink, looking at each other, looking up, one eye closed, oc only, featured image, wide eyes, derpibooru exclusive, smiling, artist:justisanimation, oc:upvote, oc:downvote, derpibooru ponified, downvote's downvotes, shook, this will end in pain, ear twitch, shocked, horrified, shrunken pupils, wall of tags" + }, + { + "url": "https://derpicdn.net/img/view/2014/8/18/702641.png", + "sources": [ + "http://ukulilia.deviantart.com/art/Coffee-to-stay-awake-all-night-475492803" + ], + "tag_input": "smug, safe, realistic, princess luna, artist:mykegreywolf, pony, mug, technically advanced, makeup, magic, female, looking at you, translated in the comments, alternate hairstyle, lips, high res, german, eyeshadow, detailed, cute, alicorn, artist:katputze, collaboration, coffee mug, coffee, beautiful, photoshop elements, smiling, lidded eyes, lunabetes, featured image, 2014, telekinesis, solo" + }, + { + "url": "https://derpicdn.net/img/view/2019/5/10/2035594.gif", + "sources": [ + "https://twitter.com/nastylittlepest/status/1126899230151467008" + ], + "tag_input": "sweet dreams fuel, frame by frame, ocbetes, lidded eyes, smiling, artist:angrylittlerodent, oc:pezzhorse, wholesome, rodent is trying to murder us, oc:rodentmod, snuggling, sleeping, safe, precious, pony, pillow, oc, nuzzling, mare, male, hug, hnnng, gif, frown, floppy ears, female, eyes closed, earth pony, ear twitch, dark, daaaaaaaaaaaw, cute, cuddling, couple, blanket, dead source, bed, duo, animated, :<, stallion, unicorn, ear fluff, oc only, featured image, weapons-grade cute" + }, + { + "url": "https://derpicdn.net/img/view/2012/1/2/0.jpg", + "sources": [ + "" + ], + "tag_input": "chair, eyes, female, grin, hilarious in hindsight, adventure in the comments, image macro, derpibooru legacy, cigar, building, smiling, featured image, gritted teeth, swinging, stallion, spider-man, smoking, sitting, safe, pony, ponified, phone, pegasus, parody, paper, necktie, muffin, meme, mare, male, mail, letter, j. jonah jameson, it begins, derpy hooves, bag, song in the comments, artist needed" + }, + { + "url": "https://derpicdn.net/img/view/2015/9/26/988000.gif", + "sources": [ + "https://derpibooru.org/988000" + ], + "description": "Fairly large GIF (~23MB), use to test WebM stuff.", + "tag_input": "alicorn, angry, animated, art, artist:assasinmonkey, artist:equum_amici, badass, barrier, crying, dark, epic, female, fight, force field, glare, glow, good vs evil, lord tirek, low angle, magic, mare, messy mane, metal as fuck, perspective, plot, pony, raised hoof, safe, size difference, spread wings, stomping, twilight's kingdom, twilight sparkle, twilight sparkle (alicorn), twilight vs tirek, underhoof" + }, + { + "url": "https://derpicdn.net/img/2012/1/2/25/large.png", + "sources": [ + "https://derpibooru.org/25" + ], + "tag_input": "artist:moe, canterlot, castle, cliff, cloud, detailed background, fog, forest, grass, mountain, mountain range, nature, no pony, outdoors, path, river, safe, scenery, scenery porn, signature, source needed, sunset, technical advanced, town, tree, useless source url, water, waterfall, widescreen, wood" + }, + { + "url": "https://derpicdn.net/img/2018/6/28/1767886/full.webm", + "sources": [ + "http://hydrusbeta.deviantart.com/art/Gleaming-in-the-Sun-Our-Colors-Shine-in-Every-Hue-611497309" + ], + "tag_input": "3d, animated, architecture, artist:hydrusbeta, castle, cloud, crystal empire, crystal palace, flag, flag waving, no pony, no sound, safe, scenery, webm" + }, + { + "url": "https://derpicdn.net/img/view/2015/2/19/832750.jpg", + "sources": [ + "http://sovietrussianbrony.tumblr.com/post/111504505079/this-image-actually-took-me-ages-to-edit-the" + ], + "tag_input": "artist:rhads, artist:the sexy assistant, canterlot, cloud, cloudsdale, cloudy, edit, lens flare, no pony, ponyville, rainbow, river, safe, scenery, sweet apple acres" + }, + { + "url": "https://derpicdn.net/img/view/2016/3/17/1110529.jpg", + "sources": [ + "https://www.deviantart.com/devinian/art/Commission-Crystals-of-thy-heart-511134926" + ], + "tag_input": "artist:devinian, aurora crystialis, bridge, cloud, crepuscular rays, crystal empire, crystal palace, edit, flower, forest, grass, log, mountain, no pony, river, road, safe, scenery, scenery porn, source needed, stars, sunset, swing, tree, wallpaper" + }, + { + "url": "https://derpicdn.net/img/view/2019/6/16/2067468.svg", + "sources": [ + "https://derpibooru.org/2067468" + ], + "tag_input": "artist:cheezedoodle96, babs seed, bloom and gloom, cutie mark, cutie mark only, no pony, safe, scissors, simple background, svg, .svg available, transparent background, vector" + } +] diff --git a/priv/repo/seeds/dev/pages.json b/priv/repo/seeds/dev/pages.json new file mode 100644 index 000000000..cdb5232e9 --- /dev/null +++ b/priv/repo/seeds/dev/pages.json @@ -0,0 +1,70 @@ +[ + { + "title": "About Philomena", + "slug": "about" + }, + { + "title": "Advertising on this Site", + "slug": "advertising" + }, + { + "title": "API Documentation", + "slug": "api" + }, + { + "title": "Approval Queue", + "slug": "approval" + }, + { + "title": "Contact Us", + "slug": "contact" + }, + { + "title": "Donations", + "slug": "donations" + }, + { + "title": "Frequently Asked Questions", + "slug": "faq" + }, + { + "title": "Markdown Syntax Guide", + "slug": "markdown" + }, + { + "title": "Privacy Policy", + "slug": "privacy" + }, + { + "title": "Site Rules", + "slug": "rules" + }, + { + "title": "Search Syntax", + "slug": "search_syntax" + }, + { + "title": "Keyboard Shortcuts", + "slug": "shortcuts" + }, + { + "title": "Spoiler Guidelines", + "slug": "spoilers" + }, + { + "title": "Getting Started", + "slug": "start" + }, + { + "title": "Tag Help", + "slug": "tags" + }, + { + "title": "Takedown Policy", + "slug": "takedowns" + }, + { + "title": "Uploading Help", + "slug": "uploading" + } +] diff --git a/priv/repo/seeds/dev/users.json b/priv/repo/seeds/dev/users.json new file mode 100644 index 000000000..338183387 --- /dev/null +++ b/priv/repo/seeds/dev/users.json @@ -0,0 +1,32 @@ +[ + { + "name": "Hot Pocket Consumer", + "email": "moderator@example.com", + "password": "philomena123", + "role": "moderator" + }, + { + "name": "Hoping For a Promotion", + "email": "assistant@example.com", + "password": "philomena123", + "role": "assistant" + }, + { + "name": "Pleb", + "email": "user@example.com", + "password": "philomena123", + "role": "user" + }, + { + "name": "Artist", + "email": "artist@example.com", + "password": "philomena123", + "role": "user" + }, + { + "name": "Lurker", + "email": "lurker@example.com", + "password": "philomena123", + "role": "user" + } +] diff --git a/priv/repo/seeds/pages/about.md b/priv/repo/seeds/pages/about.md new file mode 100644 index 000000000..66a3ac742 --- /dev/null +++ b/priv/repo/seeds/pages/about.md @@ -0,0 +1,7 @@ +# About Philomena + +Philomena is state of the art software for powering image boorus (image sharing/commenting/voting sites). At the forefront of its goals is to be the easiest-to-use and most intuitive imageboard software around. It is also designed to be easy to scale on a technical level, requiring less system resources than other booru software, saving you the hassle of getting big servers with serious hardware. + +It was initially written for [Derpibooru](https://derpibooru.org), the largest image sharing site aimed at the fans of the My Little Pony cartoon, from scratch with a view to making a really good web application for sharing images. It has since outgrown Derpibooru, and now powers many other image sharing sites, such as [Furbooru](https://furbooru.org). + +We hope you enjoy using this software! Please make sure to share any suggestions and report any issues you may find with it [in the issues section of our GitHub repository](https://github.com/philomena-dev/philomena/issues). diff --git a/priv/repo/seeds/pages/advertising.md b/priv/repo/seeds/pages/advertising.md new file mode 100644 index 000000000..67818b667 --- /dev/null +++ b/priv/repo/seeds/pages/advertising.md @@ -0,0 +1,11 @@ +# Advertising + +This is the default advertisement policy page of Philomena! Edit this with your site's advertising prices, requirements and contact details. A good advertisement page mentions what kind of advertisements are accepted (e.g. community ads only), NSFW advertisement policy, free advertisement policy (e.g. if you'd like to offer free short-term ads to independent artists), paid advertisement policy (pricing per month, terms, required advance notice, etc), technical requirements of the advertisement banner image (see below), and anything else you might want a potential advertiser to know. Don't forget to mention how a potential advertiser might contact you about advertisement inquiries. + +Philomena is designed to accept advertisements with the following parameters: + +**File size:** no larger than 500 kilobytes (500000 bytes) +**Format:** PNG, GIF or JPG/JPEG +**Max resolution:** 729x91 pixels +**Min resolution:** 699x79 pixels +(we suggest 728x90 resolution for optimal experience) diff --git a/priv/repo/seeds/pages/api.md b/priv/repo/seeds/pages/api.md new file mode 100644 index 000000000..d896f7f44 --- /dev/null +++ b/priv/repo/seeds/pages/api.md @@ -0,0 +1,1156 @@ +We provide a JSON API for major site functionality, which can be freely used by anyone wanting to produce tools for the site or other web applications that use the data provided within this website. + +## Licensing + +Anyone may use the API. Users making abusively high numbers of requests or excessively expensive requests will be asked to stop, and banned if they do not. Your application must properly cache, and respect server-side cache expiry times. Your client must gracefully back off if requests fail, preferably exponentially or fatally. + +If images are used, the artist must always be credited (if provided) and the original source URL must be displayed alongside the image, either in textual form or as a link. A link to the image page on this website is optional but recommended. The `https:` protocol must be specified on all URLs. + +## Parameters + +This is a list of general parameters that are useful when working with the API. Not all parameters may be used in every request. + +Name | Description +--- | --- +`filter_id` Assuming | the user can access the filter ID given by the parameter, overrides the current filter for this request. This is primarily `useful` | for unauthenticated API access. +`key` | An optional authentication token. If omitted, no user will be authenticated. You can find your authentication token in your [account settings](/registration/edit). +`page` | Controls the current page of the response, if the response is paginated. Empty values default to the first page. +`per_page` | Controls the number of results per page, up to a limit of 50, if the response is paginated. The default is 25. +`q` | The current search query, if the request is a search request. +`sd` | The current sort direction, if the request is a search request. +`sf` | The current sort field, if the request is a search request. + +## Routes + +The interested reader may find the implementations of these endpoints [here](https://github.com/derpibooru/philomena/tree/master/lib/philomena_web/controllers/api). For the purposes of this document, a brief overview is given. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodPathAllowed Query ParametersDescriptionResponse FormatExample
GET/api/v1/json/comments/:comment_idFetches a comment response for the comment ID referenced by the comment_id URL parameter.{"comment":comment-response}/api/v1/json/comments/1
GET/api/v1/json/images/:image_idkey, filter_idFetches an image response for the image ID referenced by the image_id URL parameter.{"image":image-response}/api/v1/json/images/1
POST/api/v1/json/imageskey, urlSubmits a new image. Both key and url are required. Errors will result in an {"errors":image-errors-response}.{"image":image-response}Posting images
GET/api/v1/json/images/featuredFetches an image response for the for the current featured image.{"image":image-response}/api/v1/json/images/featured
GET/api/v1/json/tags/:tag_idFetches a tag response for the tag slug given by the tag_id URL parameter. The tag's ID is not used.{"tag":tag-response}/api/v1/json/tags/artist-colon-atryl
GET/api/v1/json/posts/:post_idFetches a post response for the post ID given by the post_id URL parameter.{"post":post-response}/api/v1/json/posts/2730144
GET/api/v1/json/profiles/:user_idFetches a profile response for the user ID given by the user_id URL parameter.{"user":user-response}/api/v1/json/profiles/1
GET/api/v1/json/filters/:filter_idkeyFetches a filter response for the filter ID given by the filter_id URL parameter.{"filter":filter-response}/api/v1/json/filters/1
GET/api/v1/json/filters/systempageFetches a list of filter responses that are flagged as being system filters (and thus usable by anyone).{"filters":[filter-response]}/api/v1/json/filters/system
GET/api/v1/json/filters/userkey, pageFetches a list of filter responses that belong to the user given by key. If no key is given or it is invalid, will return a 403 Forbidden error.{"filters":[filter-response]}/api/v1/json/filters/user
GET/api/v1/json/oembedurlFetches an oEmbed response for the given app link or CDN URL.oembed-response/api/v1/json/oembed?url=https://cdn.philomena.local/img/2012/1/2/3/full.png
GET/api/v1/json/search/commentskey, pageExecutes the search given by the q query parameter, and returns comment responses sorted by descending creation time.{"comments":[comment-response]}/api/v1/json/search/comments?q=image_id:1000000
GET/api/v1/json/search/gallerieskey, pageExecutes the search given by the q query parameter, and returns gallery responses sorted by descending creation time.{"galleries":[gallery-response]}/api/v1/json/search/galleries?q=title:mean*
GET/api/v1/json/search/postskey, pageExecutes the search given by the q query parameter, and returns post responses sorted by descending creation time.{"posts":[post-response]}/api/v1/json/search/posts?q=subject:time wasting thread
GET/api/v1/json/search/imageskey, filter_id, page, per_page, q, sd, sfExecutes the search given by the q query parameter, and returns image responses.{"images":[image-response]}/api/v1/json/search/images?q=safe
GET/api/v1/json/search/tagspageExecutes the search given by the q query parameter, and returns tag responses sorted by descending image count.{"tags":[tag-response]}/api/v1/json/search/tags?q=analyzed_name:wing
POST/api/v1/json/search/reversekey, url, distanceReturns image responses based on the results of reverse-searching the image given by the url query parameter.{"images":[image-response]}/api/v1/json/search/reverse?url=https://cdn.philomena.local/img/2019/12/24/2228439/full.jpg
GET/api/v1/json/forumsFetches a list of forum responses.{"forums":forum-response}/api/v1/json/forums
GET/api/v1/json/forums/:short_nameFetches a forum response for the abbreviated name given by the short_name URL parameter.{"forum":forum-response}/api/v1/json/forums/dis
GET/api/v1/json/forums/:short_name/topicspageFetches a list of topic responses for the abbreviated forum name given by the short_name URL parameter.{"topics":topic-response}/api/v1/json/forums/dis/topics
GET/api/v1/json/forums/:short_name/topics/:topic_slugFetches a topic response for the abbreviated forum name given by the short_name and topic given by topic_slug URL parameters.{"topic":topic-response}/api/v1/json/forums/dis/topics/ask-the-mods-anything
GET/api/v1/json/forums/:short_name/topics/:topic_slug/postspageFetches a list of post responses for the abbreviated forum name given by the short_name and topic given by topic_slug URL parameters.{"posts":post-response}/api/v1/json/forums/dis/topics/ask-the-mods-anything/posts
GET/api/v1/json/forums/:short_name/topics/:topic_slug/posts/:post_idFetches a post response for the abbreviated forum name given by the short_name, topic given by topic_slug and post given by post_id URL parameters.{"post":post-response}/api/v1/json/forums/dis/topics/ask-the-mods-anything/posts/2761095
+ +## Posting Images + +Posting images should be done via request body parameters. An example with all parameters included is shown below. + +You are *strongly recommended* to test code using this endpoint using a local copy of the website's source code. Abuse of the endpoint **will result in a ban**. + +You *must* provide the direct link to the image in the `url` parameter. + +You *must* set the `content-type` header to `application/json` for the site to process your request. + +``` +POST /api/v1/json/images?key=API_KEY +``` +``` +{ +"image": { + "description": "[bq]Hey there this is a test post![/bq]\nDescriptions are *weird*.\nHave a >>0 re-upload :)\n", + "tag_input": "artist needed, safe, derpy hooves, pegasus, pony, adventure in the comments, bag, building, chair, cigar, derpibooru legacy, eyes, featured image, female, grin, gritted teeth, hilarious in hindsight, image macro, it begins, j. jonah jameson, letter, mail, male, mare, meme, muffin, necktie, paper, parody, phone, ponified, sitting, smiling, smoking, song in the comments, spider-man, stallion, swinging", + "source_url": "https://philomena.local/images/0" +}, +"url": "https://cdn.philomena.local/img/view/2012/1/2/0.jpg" +} +``` + +

Image Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
animatedBooleanWhether the image is animated.
aspect_ratioFloatThe image's width divided by its height.
comment_countIntegerThe number of comments made on the image.
created_atRFC3339 datetimeThe creation time, in UTC, of the image.
deletion_reasonStringThe hide reason for the image, or null if none provided. This will only have a value on images which are deleted for a rule violation.
descriptionStringThe image's description.
downvotesIntegerThe number of downvotes the image has.
duplicate_ofIntegerThe ID of the target image, or null if none provided. This will only have a value on images which are merged into another image.
durationFloatThe number of seconds the image lasts, if animated, otherwise .04.
favesIntegerThe number of faves the image has.
first_seen_atRFC3339 datetimeThe time, in UTC, the image was first seen (before any duplicate merging).
formatStringThe file extension of the image. One of "gif", "jpg", "jpeg", "png", "svg", "webm".
heightIntegerThe image's height, in pixels.
hidden_from_usersBooleanWhether the image is hidden. An image is hidden if it is merged or deleted for a rule violation.
idIntegerThe image's ID.
intensitiesObjectOptional object of internal image intensity data for deduplication purposes. May be null if intensities have not yet been generated.
mime_typeStringThe MIME type of this image. One of "image/gif", "image/jpeg", "image/png", "image/svg+xml", "video/webm".
nameStringThe filename that the image was uploaded with.
orig_sha512_hashStringThe SHA512 hash of the image as it was originally uploaded.
processedBooleanWhether the image has finished optimization.
representationsObjectA mapping of representation names to their respective URLs. Contains the keys "full", "large", "medium", "small", "tall", "thumb", "thumb_small", "thumb_tiny".
scoreIntegerThe image's number of upvotes minus the image's number of downvotes.
sha512_hashStringThe SHA512 hash of this image after it has been processed.
sizeIntegerThe number of bytes the image's file contains.
source_urlString(Deprecated - Use source_urls field instead) Provides the first source URL of the image as stored in the database, intended for legacy applications only.
source_urlsString[]A list of all source URLs provided for the image, may be empty.
spoileredBooleanWhether the image is hit by the current filter.
tag_countIntegerThe number of tags present on the image.
tag_idsArrayA list of tag IDs the image contains.
tagsArrayA list of tag names the image contains.
thumbnails_generatedBooleanWhether the image has finished thumbnail generation. Do not attempt to load images from view_url or representations if this is false.
updated_atRFC3339 datetimeThe time, in UTC, the image was last updated.
uploaderStringThe image's uploader.
uploader_idIntegerThe ID of the image's uploader. null if uploaded anonymously.
upvotesIntegerThe image's number of upvotes.
view_urlStringThe image's view URL, including tags.
widthIntegerThe image's width, in pixels.
wilson_scoreFloatThe lower bound of the Wilson score interval for the image, based on its upvotes and downvotes, given a z-score corresponding to a confidence of 99.5%.
+

Comment Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
authorStringThe comment's author.
avatarStringThe URL of the author's avatar. May be a link to the CDN path, or a data: URI.
bodyStringThe comment text.
created_atRFC3339 datetimeThe creation time, in UTC, of the comment.
edit_reasonStringThe edit reason for this comment, or null if none provided.
edited_atRFC3339 datetimeThe time, in UTC, this comment was last edited at, or null if it was not edited.
idIntegerThe comment's ID.
image_idIntegerThe ID of the image the comment belongs to.
updated_atRFC3339 datetimeThe time, in UTC, the comment was last updated at.
user_idIntegerThe ID of the user the comment belongs to, if any.
+

Forum Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
nameStringThe forum's name.
short_nameStringThe forum's short name (used to identify it).
descriptionStringThe forum's description.
topic_countIntegerThe amount of topics in the forum.
post_countIntegerThe amount of posts in the forum.
+

Topic Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
slugStringThe topic's slug (used to identify it).
titleStringThe topic's title.
post_countIntegerThe amount of posts in the topic.
view_countIntegerThe amount of views the topic has received.
stickyBooleanWhether the topic is sticky.
last_replied_to_atRFC3339 datetimeThe time, in UTC, when the last reply was made.
lockedBooleanWhether the topic is locked.
user_idIntegerThe ID of the user who made the topic. null if posted anonymously.
authorStringThe name of the user who made the topic.
+

Post Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
authorStringThe post's author.
avatarStringThe URL of the author's avatar. May be a link to the CDN path, or a data: URI.
bodyStringThe post text.
created_atRFC3339 datetimeThe creation time, in UTC, of the post.
edit_reasonStringThe edit reason for this post.
edited_atRFC3339 datetimeThe time, in UTC, this post was last edited at, or null if it was not edited.
idIntegerThe post's ID (used to identify it).
updated_atRFC3339 datetimeThe time, in UTC, the post was last updated at.
user_idIntegerThe ID of the user the post belongs to, if any.
+

Tag Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
aliased_tagStringThe slug of the tag this tag is aliased to, if any.
aliasesArrayThe slugs of the tags aliased to this tag.
categoryStringThe category class of this tag. One of "character", "content-fanmade", "content-official", "error", "oc", "origin", "rating", "species", "spoiler".
descriptionStringThe long description for the tag.
dnp_entriesArrayAn array of objects containing DNP entries claimed on the tag.
idIntegerThe tag's ID.
imagesIntegerThe image count of the tag.
implied_by_tagsArrayThe slugs of the tags this tag is implied by.
implied_tagsArrayThe slugs of the tags this tag implies.
nameStringThe name of the tag.
name_in_namespaceStringThe name of the tag in its namespace.
namespaceStringThe namespace of the tag.
short_descriptionStringThe short description for the tag.
slugStringThe slug for the tag.
spoiler_image_uriStringThe spoiler image for the tag.
+

User Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
idIntegerThe ID of the user.
nameStringThe name of the user.
slugStringThe slug of the user.
roleStringThe role of the user.
descriptionStringThe description (bio) of the user.
avatar_urlStringThe URL of the user's thumbnail. null if the avatar is not set.
created_atRFC3339 datetimeThe creation time, in UTC, of the user.
comments_countIntegerThe comment count of the user.
uploads_countIntegerThe upload count of the user.
posts_countIntegerThe forum posts count of the user.
topics_countIntegerThe forum topics count of the user.
linksObjectThe links the user has registered. See links-response.
awardsObjectThe awards/badges of the user. See awards-response.
+

Filter Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
idIntegerThe id of the filter.
nameStringThe name of the filter.
descriptionStringThe description of the filter.
user_idIntegerThe id of the user the filter belongs to. null if it isn't assigned to a user (usually system filters only).
user_countIntegerThe amount of users employing this filter.
systemBooleanIf true, is a system filter. System filters are usable by anyone and don't have a user_id set.
publicBooleanIf true, is a public filter. Public filters are usable by anyone.
spoilered_tag_idsArrayA list of tag IDs (as integers) that this filter will spoil.
spoilered_complexStringThe complex spoiled filter.
hidden_tag_idsArrayA list of tag IDs (as integers) that this filter will hide.
hidden_complexStringThe complex hidden filter.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
user_idIntegerThe ID of the user who owns this link.
created_atRFC3339 datetimeThe creation time, in UTC, of this link.
stateStringThe state of this link.
tag_idIntegerThe ID of an associated tag for this link. null if no tag linked.
+

Awards Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
image_urlStringThe URL of this award.
titleStringThe title of this award.
idIntegerThe ID of the badge this award is derived from.
labelStringThe label of this award.
awarded_onRFC3339 datetimeThe time, in UTC, when this award was given.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
descriptionStringThe gallery's description.
idIntegerThe gallery's ID.
spoiler_warningStringThe gallery's spoiler warning.
thumbnail_idIntegerThe ID of the cover image for the gallery.
titleStringThe gallery's title.
userStringThe name of the gallery's creator.
user_idIntegerThe ID of the gallery's creator.
+

Image Errors Responses

+

Each field is optional and is an Array of Strings.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
imageArrayErrors in the submitted image
image_aspect_ratioArrayErrors in the submitted image
image_formatArrayWhen an image is unsupported (ex. WEBP)
image_heightArrayErrors in the submitted image
image_widthArrayErrors in the submitted image
image_sizeArrayUsually if an image that is too large is uploaded.
image_is_animatedArrayErrors in the submitted image
image_mime_typeArrayErrors in the submitted image
image_orig_sha512_hashArrayErrors in the submitted image. If has already been taken is present, means the image already exists in the database.
image_sha512_hashArrayErrors in the submitted image
tag_inputArrayErrors with the tag metadata.
uploaded_imageArrayErrors in the submitted image
+

Oembed Responses

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
author_nameStringThe comma-delimited names of the image authors.
author_urlStringThe source URL of the image.
cache_ageIntegerAlways 7200.
derpibooru_commentsIntegerThe number of comments made on the image.
derpibooru_idIntegerThe image's ID.
derpibooru_scoreIntegerThe image's number of upvotes minus the image's number of downvotes.
derpibooru_tagsArrayThe names of the image's tags.
provider_nameStringAlways "Derpibooru".
provider_urlStringAlways "https://derpibooru.org".
titleStringThe image's ID and associated tags, as would be given on the title of the image page.
typeStringAlways "photo".
versionStringAlways "1.0".
diff --git a/priv/repo/seeds/pages/approval.md b/priv/repo/seeds/pages/approval.md new file mode 100644 index 000000000..5247f1c29 --- /dev/null +++ b/priv/repo/seeds/pages/approval.md @@ -0,0 +1,43 @@ +# Approval Queue and User Verification + +If you are here, you must be wondering why your upload got held up in an approval queue instead of being posted directly. + +Shortly speaking - **this is to protect our users from illegal imagery.** + +# Images + +#### **What happens to the image while it's not approved?** + +The image will not appear in any search results and will be impossible to be linked to via our on-site image embedding syntax. It is still viewable via a direct URL. + +#### **Why did my upload require approval?** + +We require that all uploads from users without an account, as well as unverified registered users go through the approval queue where a staff member can decide whether it's illegal content (Rule #5) or not. + +#### **Will my image lose views because of this?** + +No! Once approved, your image appears in search results and on the home page as if it was uploaded at the time of approval. + +#### **Is this some kind of censorship?** + +No. We are strictly checking for whether or not the imagery is illegal (Rule #5) or not. This is not "Quality Control" in any way whatsoever. + +#### **How do I get verified?** + +First of all - register an account. Once you upload a certain (small) amount of images that get approved by the staff members, your account will be evaluated and depending on staff evaluation, you will be granted verification. Once verified, your uploads will bypass the approval queue and be automatically approved. Please note that staff evaluation of accounts may take up to a week, but will usually take a day or two. + +#### **Where can I check if I'm verified?** + +As of the moment of writing this article - you cannot. If you're unsure if you're verified or not, simply ask a staff member via PMs. + +# Comments and forum posts + +Comments and forum posts are also subject to additional moderation measures now. + +#### **Why can't I embed external images into my comments or posts?** + +Users without an account cannot use the image embed syntax (`![](image url)`) at all. + +#### **Why does my comment/post require additional approval?** + +If your account is relatively new, any of your posts/comments that contain the image embed syntax (`![](image url)`) must go through staff approval before they're publicly visible and searchable. diff --git a/priv/repo/seeds/pages/contact.md b/priv/repo/seeds/pages/contact.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/contact.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/donations.md b/priv/repo/seeds/pages/donations.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/donations.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/faq.md b/priv/repo/seeds/pages/faq.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/faq.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/markdown.md b/priv/repo/seeds/pages/markdown.md new file mode 100644 index 000000000..2606c5200 --- /dev/null +++ b/priv/repo/seeds/pages/markdown.md @@ -0,0 +1,395 @@ +This page is here to help you get a better grasp on the syntax of Markdown, the text processing engine this site uses. + +## Inline formatting +Inline formatting is the most commonly seen type of text formatting in Markdown. It can be applied almost anywhere else and doesn't depend on specific context (most of the time). + +Operator | Example | Result +--- | --- | --- +Bold | `This is **huge**` | This is **huge** +Italic | `*very* clever, Connor... _very..._` | *very* clever, Connor... _very..._ +Underline | `And I consider this __important__` | And I consider this __important__ +Strikethrough | `I am ~~wrong~~ right` | I am ~~wrong~~ right +Superscript | `normal text ^superscripted text^` | normal text ^superscripted text^ +Subscript | `normal text %subscripted text%` | normal text %subscripted text% +Spoiler | `Psst! ||Darth Vader is Luke's father||` | Psst! ||Darth Vader is Luke's father|| +Code | ```Use `**bold**` to make text bold!``` | Use `**bold**` to make text bold! + +#### Multi-line inlines + +Most inline formatting can extend beyond just a single line and travel to other lines. However, it does have certain quirks, especially if you're unused to the Markdown syntax. + +``` +**I am a very +bold text** +``` +
+
+ Result +
+
+
+ I am a very
bold text
+
+
+
+ +However, if you try to insert a newline in the middle of it, it won't work. + +``` +**I am not a very + +bold text** +``` + +
+
+ Result +
+
+
**I am not a very
+
bold text**
+
+
+ +If you really need an empty line in the middle of your inline-formatted text, you must *escape* the line ending. In order to do so, Markdown provides us with the `\` (backslash) character. Backslash is a very special character and is used for *escaping* other special characters. *Escaping* forces the character immediately after the backslash to be ignored by the parser. + +As such, we can write our previous example like so to preserve the empty line: +``` +**I am a very +\ +bold text** +``` +
+
+ Result +
+
+
I am a very
+
+ bold text
+
+
+ +#### Combining inlines +Most inline operators may be combined with each other (with the exception of the ````code```` syntax). + +``` +_I am an italic text **with some bold in it**._ +``` + +
+
+ Result +
+
+
I am an italic text with some bold in it.
+
+
+ + +## Block formatting +Block formatting is the kind of formatting that cannot be written within a single line and typically requires to be written on its own line. Many block formatting styles extend past just one line. + +#### Blockquotes +Philomena's flavor of Markdown makes some changes to the blockquote syntax compared to regular CommonMark. The basic syntax is a > followed by a space. + +``` +> quote text +``` + +> quote text + +--- + +Please note, that if > is not followed by a space, it will not become a blockquote! + +``` +>not a quote +``` + +>not a quote + +--- + +Same goes for >>, even if followed by a space. + +``` +>> not a quote +``` + +>> not a quote + +--- + +You may continue a quote by adding > followed by a space on a new line, even if the line is otherwise empty. + +``` +> quote text +> +> continuation of quote +``` + +> quote text +> +> continuation of quote + +--- + +To nest a quote, simply repeat > followed by a space as many times as you wish to have nested blockquotes. + +``` +> quote text +> > nested quote +> > > even deeper nested quote +``` + +> quote text +> > nested quote +> > > even deeper nested quote + +#### Headers +Markdown supports adding headers to your text. The syntax is # repeated up to 6 times. + +``` +# Header 1 +## Header 2 +### Header 3 +#### Header 4 +##### Header 5 +###### Header 6 +``` + +# Header 1 +## Header 2 +### Header 3 +#### Header 4 +##### Header 5 +###### Header 6 + +#### Code block +Another way to write code is by writing a code block. Code blocks, unlike inline code syntax, are styled similar to blockquotes and are more appropriate for sharing larger snippets of code. In fact, this very page has been using this very structure to show examples of code. + +~~~ +``` +
+

Hello World!

+
+``` +~~~ + +``` +
+

Hello World!

+
+``` + +Code blocks may also use tildes (\~) instead of backticks (\`). + +``` +~~~ +code block +~~~ +``` + +~~~ +code block +~~~ + +## Links +Links have the basic syntax of + +``` +[Link Text](https://example.com) +``` + +[Link Text](https://example.com) + +Most links pasted as plaintext will be automatically converted into a proper clickable link, as long as they don't begin with dangerous protocols. +As such... + +``` +https://example.com +``` + +https://example.com + +On-site links may be written as either a relative or absolute path. If the on-site link is written as the absolute path, it will be automatically converted into a relative link for the convenience of other users. + +``` +[Link to the first image](https://philomena.local/images/0) +[Link to the first image](/images/0) +``` + +[Link to the first image](https://philomena.local/images/0) +[Link to the first image](/images/0) + +## On-site images +If you wish to link an on-site image, you should use the >>:id syntax. It respects filters currently in-use by the reader and spoilers content they do not wish to see. +**You should always use this for on-site uploads!** (as this will let other users filter the image if they wish to, and it is against the rules to not show content with care) +Here's a brief explanation of its usage. + +Operator | Description of result +--- | --- +\>\>5 | Simple link to image +\>\>5s | Small (150x150) thumbnail of the image +\>\>5t | Regular (320x240) thumbnail of the image +\>\>5p | Preview (800x600) size of the image + +>>5 +>>5s +>>5t +>>5p + +## External images +Some images you may wish to link may not exist on the site. To link them Markdown provides us with a special syntax. All images embedded this way are proxied by our image proxy (Go-Camo). + +``` +![](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +``` + +![](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) + +You may control the size of your externally-linked image by specifying the alt text. Certain keywords are recognized as size modifiers. The modifiers are case-sensitive! + +Modifier | Resulting size +--- | --- +tiny | 64x64 +small | 128x128 +medium | 256x256 +large | 512x512 +(anything else) | (actual size of the image) + +``` +![tiny](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![small](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![medium](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![large](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +``` + +![tiny](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![small](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![medium](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![large](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) +![](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg) + +#### Image links +To make an image link, simply combine the external image syntax with the link syntax. + +``` +[![](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg)](https://github.com/philomena-dev/philomena) +``` + +[![](https://raw.githubusercontent.com/philomena-dev/philomena/master/assets/static/images/phoenix.svg)](https://github.com/philomena-dev/philomena) + +## Lists +#### Unordered list +Unordered lists can be written fairly intuitively, by putting one of the special characters in front of each line that should be a part of the list. + +``` +Shopping list: +* Milk +* Eggs +* Soda +``` + +Shopping list: +* Milk +* Eggs +* Soda + +You may use any of the following characters at the beginning of the line to make an unordered list: + +``` +* ++ +- +``` + +Lists may be nested and have sublists within them. Simply prefix your sublist items with three spaces while within another list. + +``` +* Item one +* Item two + * Sublist item one + * Sublist item two +``` + +* Item one +* Item two + * Sublist item one + * Sublist item two + +#### Ordered list +To write an ordered list, simply put a number at the beginning of the line followed by a dot or closing bracket. It doesn't actually matter which order your numbers are written in, the list will always maintain its incremental order. Note the 4 in the example, it isn't a typo. + +``` +1. Item one +2. Item two +4. Item three +``` + +1. Item one +2. Item two +4. Item three + +**Ordered lists cannot be sublists to other ordered lists.** They can, however, be sublists to unordered lists. Unordered lists, in turn, may be sublists in ordered lists. + +``` +1) Item one +2) Item two + * Sublist item one + * Sublist item two +``` + +1) Item one +2) Item two + * Sublist item one + * Sublist item two + +## Tables +Philomena's Markdown implementation supports GitHub-style tables. This isn't a part of the core Markdown specification, but we support them. The colons are used to specify the alignment of columns. + +``` +| Left | Center | Right | +| ------------ |:--------------:| -------------:| +| left-aligned | center-aligned | right-aligned | +| *formatting* | **works** | __here__ | +``` + +| Left | Center | Right | +| ------------ |:--------------:| -------------:| +| left-aligned | center-aligned | right-aligned | +| *formatting* | **works** | __here__ | + +In tables, the pipes (|) at the edges of the table are optional. To separate table head from body, you need to put in at least three - symbols. As such, example above could have also been written like so: + +``` +Left | Center | Right +--- | :---: | ---: +left-aligned | center-aligned | right-aligned +*formatting* | **works** | __here__ +``` + +Left | Center | Right +--- | :---: | ---: +left-aligned | center-aligned | right-aligned +*formatting* | **works** | __here__ + +# Escaping the syntax. + +Sometimes you may wish certain characters to not be interpreted as Markdown syntax. This is where the backslash comes in! Prefixing any markup with a backslash will cause the markup immediately following the backslash to not be parsed, for example: + +``` +\*\*grr grr, I should not be bold!\*\* +``` + +\*\*grr grr, I should not be bold\*\* + +Code blocks and code inlines will also escape the syntax to a limited extent (except for backticks themselves). + +``` +`**not bold!**` +``` + +`**not bold!**` diff --git a/priv/repo/seeds/pages/privacy.md b/priv/repo/seeds/pages/privacy.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/privacy.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/rules.md b/priv/repo/seeds/pages/rules.md new file mode 100644 index 000000000..e69de29bb diff --git a/priv/repo/seeds/pages/search_syntax.md b/priv/repo/seeds/pages/search_syntax.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/search_syntax.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/shortcuts.md b/priv/repo/seeds/pages/shortcuts.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/shortcuts.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/spoilers.md b/priv/repo/seeds/pages/spoilers.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/spoilers.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/start.md b/priv/repo/seeds/pages/start.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/start.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/tags.md b/priv/repo/seeds/pages/tags.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/tags.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/takedowns.md b/priv/repo/seeds/pages/takedowns.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/takedowns.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds/pages/uploading.md b/priv/repo/seeds/pages/uploading.md new file mode 100644 index 000000000..4f2e970e9 --- /dev/null +++ b/priv/repo/seeds/pages/uploading.md @@ -0,0 +1 @@ +PLACEHOLDER diff --git a/priv/repo/seeds.json b/priv/repo/seeds/seeds.json similarity index 98% rename from priv/repo/seeds.json rename to priv/repo/seeds/seeds.json index 3c5f06d9a..8d975300b 100644 --- a/priv/repo/seeds.json +++ b/priv/repo/seeds/seeds.json @@ -25,7 +25,7 @@ }, { "name": "Shows and Movies", - "short_name": "pony", + "short_name": "shows", "description": "Discuss TV shows and movies, as well as their characters and theories.", "access_level": "normal" }, diff --git a/priv/repo/seeds/seeds_development.json b/priv/repo/seeds/seeds_development.json new file mode 100644 index 000000000..2c63c0851 --- /dev/null +++ b/priv/repo/seeds/seeds_development.json @@ -0,0 +1,2 @@ +{ +} diff --git a/priv/repo/seeds_development.exs b/priv/repo/seeds_development.exs index cde0302af..75f6333be 100644 --- a/priv/repo/seeds_development.exs +++ b/priv/repo/seeds_development.exs @@ -10,125 +10,221 @@ # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. -alias Philomena.{Repo, Forums.Forum, Users, Users.User} -alias Philomena.Comments -alias Philomena.Images -alias Philomena.Topics -alias Philomena.Posts -alias Philomena.Tags - -{:ok, ip} = EctoNetwork.INET.cast({203, 0, 113, 0}) -{:ok, _} = Application.ensure_all_started(:plug) - -resources = - "priv/repo/seeds_development.json" - |> File.read!() - |> Jason.decode!() - -IO.puts "---- Generating users" -for user_def <- resources["users"] do - {:ok, user} = Users.register_user(user_def) - - user - |> Repo.preload([:roles]) - |> User.confirm_changeset() - |> User.update_changeset(%{role: user_def["role"]}, []) - |> Repo.update!() -end +defmodule Philomena.DevSeeds do + alias Philomena.{Repo, Forums.Forum, Users, Users.User} + alias Philomena.Comments + alias Philomena.Images + alias Philomena.Images.Image + alias Philomena.Topics + alias Philomena.Posts + alias Philomena.Tags + import Ecto.Query + + def seed() do + {:ok, _} = Application.ensure_all_started(:plug) + + # resources = + # "priv/repo/seeds/seeds_development.json" + # |> File.read!() + # |> Jason.decode!() + + communications = + "priv/repo/seeds/dev/communications.json" + |> File.read!() + |> Jason.decode!() + + images = + "priv/repo/seeds/dev/images.json" + |> File.read!() + |> Jason.decode!() + + # pages = + # "priv/repo/seeds/dev/pages.json" + # |> File.read!() + # |> Jason.decode!() + + users = + "priv/repo/seeds/dev/users.json" + |> File.read!() + |> Jason.decode!() + + Logger.configure(level: :warning) + + IO.puts "---- Generating users" + for user_def <- users do + {:ok, user} = Users.register_user(user_def) + + user + |> Repo.preload([:roles]) + |> User.confirm_changeset() + |> User.update_changeset(%{role: user_def["role"]}, []) + |> Repo.update!() + end + + users = Repo.all(User) + pleb = Repo.get_by!(User, name: "Pleb") + pleb_attrs = request_attrs(pleb) + + IO.puts "---- Generating images" + for image_def <- images do + file = Briefly.create!() + now = DateTime.utc_now() |> DateTime.to_unix(:microsecond) + + IO.puts "Fetching #{image_def["url"]} ..." + {:ok, %{body: body}} = PhilomenaProxy.Http.get(image_def["url"]) + + File.write!(file, body) + + upload = %Plug.Upload{ + path: file, + content_type: "application/octet-stream", + filename: "fixtures-#{now}" + } + + IO.puts "Inserting ..." + + Images.create_image( + pleb_attrs, + Map.merge(image_def, %{"image" => upload}) + ) + |> case do + {:ok, %{image: image}} -> + Images.approve_image(image) + Images.reindex_image(image) + Tags.reindex_tags(image.added_tags) + + IO.puts "Created image ##{image.id}" + + {:error, :image, changeset, _so_far} -> + IO.inspect changeset.errors + end + end + + IO.puts "---- Generating comments for image #1" + for comment_body <- communications["demos"] do + image = Images.get_image!(1) + + Comments.create_comment( + image, + pleb_attrs, + %{"body" => comment_body} + ) + |> case do + {:ok, %{comment: comment}} -> + Comments.approve_comment(comment, pleb) + Comments.reindex_comment(comment) + Images.reindex_image(image) + + {:error, :comment, changeset, _so_far} -> + IO.inspect changeset.errors + end + end + + all_imgs = Image |> where([i], i.id > 1) |> Repo.all() + + IO.puts "---- Generating random comments for images other than 1" + for _ <- 1..1000 do + image = Enum.random(all_imgs) + user = random_user(users) + + Comments.create_comment( + image, + request_attrs(user), + %{"body" => random_body(communications)} + ) + |> case do + {:ok, %{comment: comment}} -> + Comments.approve_comment(comment, user) + Comments.reindex_comment(comment) + Images.reindex_image(image) + + {:error, :comment, changeset, _so_far} -> + IO.inspect changeset.errors + end + end + + IO.puts "---- Generating forum posts" + for _ <- 1..500 do + random_topic_no_replies(communications, users) + end + + for _ <- 1..20 do + random_topic(communications, users) + end -pleb = Repo.get_by!(User, name: "Pleb") -request_attributes = [ - fingerprint: "c1836832948", - ip: ip, - user_agent: "Hopefully not IE", - referrer: "localhost", - user_id: pleb.id, - user: pleb -] - -IO.puts "---- Generating images" -for image_def <- resources["remote_images"] do - file = Briefly.create!() - now = DateTime.utc_now() |> DateTime.to_unix(:microsecond) - - IO.puts "Fetching #{image_def["url"]} ..." - {:ok, %{body: body}} = PhilomenaProxy.Http.get(image_def["url"]) - - File.write!(file, body) - - upload = %Plug.Upload{ - path: file, - content_type: "application/octet-stream", - filename: "fixtures-#{now}" - } - - IO.puts "Inserting ..." - - Images.create_image( - request_attributes, - Map.merge(image_def, %{"image" => upload}) - ) - |> case do - {:ok, %{image: image}} -> - Images.approve_image(image) - Images.reindex_image(image) - Tags.reindex_tags(image.added_tags) - - IO.puts "Created image ##{image.id}" - - {:error, :image, changeset, _so_far} -> - IO.inspect changeset.errors + IO.puts "---- Done." + + Logger.configure(level: :debug) end -end -IO.puts "---- Generating comments for image #1" -for comment_body <- resources["comments"] do - image = Images.get_image!(1) - - Comments.create_comment( - image, - request_attributes, - %{"body" => comment_body} - ) - |> case do - {:ok, %{comment: comment}} -> - Comments.approve_comment(comment, pleb) - Comments.reindex_comment(comment) - Images.reindex_image(image) - - {:error, :comment, changeset, _so_far} -> - IO.inspect changeset.errors + defp default_ip() do + {:ok, ip} = EctoNetwork.INET.cast({203, 0, 113, 0}) + ip end -end -IO.puts "---- Generating forum posts" -for %{"forum" => forum_name, "topics" => topics} <- resources["forum_posts"] do - forum = Repo.get_by!(Forum, short_name: forum_name) + defp available_forums(), do: ["dis", "art", "rp", "meta", "shows"] + + defp random_forum(), do: Enum.random(available_forums()) + + defp random_user(users), do: Enum.random(users) + + defp request_attrs(%{id: id} = user) do + [ + fingerprint: "d015c342859dde3", + ip: default_ip(), + user_agent: "Hopefully not IE", + referrer: "localhost", + user_id: id, + user: user + ] + end + + defp random_body(%{"random" => random}) do + count = :rand.uniform(3) + + (0..count) + |> Enum.map(fn _ -> Enum.random(random) end) + |> Enum.join("\n\n") + end + + defp random_title(%{"titles" => titles}) do + Enum.random(titles["first"]) <> " " + <> Enum.random(titles["second"]) <> " " + <> Enum.random(titles["third"]) + end + + defp random_topic(comm, users) do + forum = Repo.get_by!(Forum, short_name: random_forum()) + op = random_user(users) - for %{"title" => topic_name, "posts" => [first_post | posts]} <- topics do Topics.create_topic( forum, - request_attributes, + request_attrs(op), %{ - "title" => topic_name, + "title" => random_title(comm), "posts" => %{ "0" => %{ - "body" => first_post, + "body" => random_body(comm), } } } ) |> case do {:ok, %{topic: topic}} -> - for post <- posts do + IO.puts(" -> created topic ##{topic.id}") + count = :rand.uniform(250) + 5 + + for _ <- 1..count do + user = random_user(users) + Posts.create_post( topic, - request_attributes, - %{"body" => post} + request_attrs(user), + %{"body" => random_body(comm)} ) |> case do {:ok, %{post: post}} -> - Posts.approve_post(post, pleb) + Posts.approve_post(post, op) Posts.reindex_post(post) {:error, :post, changeset, _so_far} -> @@ -136,10 +232,37 @@ for %{"forum" => forum_name, "topics" => topics} <- resources["forum_posts"] do end end + IO.puts(" -> created #{count} replies for topic ##{topic.id}") + + {:error, :topic, changeset, _so_far} -> + IO.inspect changeset.errors + end + end + + defp random_topic_no_replies(comm, users) do + forum = Repo.get_by!(Forum, short_name: random_forum()) + op = random_user(users) + + Topics.create_topic( + forum, + request_attrs(op), + %{ + "title" => random_title(comm), + "posts" => %{ + "0" => %{ + "body" => random_body(comm), + } + } + } + ) + |> case do + {:ok, %{topic: topic}} -> + IO.puts(" -> created topic ##{topic.id}") + {:error, :topic, changeset, _so_far} -> IO.inspect changeset.errors end end end -IO.puts "---- Done." +Philomena.DevSeeds.seed() diff --git a/priv/repo/seeds_development.json b/priv/repo/seeds_development.json deleted file mode 100644 index c156652a2..000000000 --- a/priv/repo/seeds_development.json +++ /dev/null @@ -1,444 +0,0 @@ -{ - "users": [{ - "name": "Hot Pocket Consumer", - "email": "moderator@example.com", - "password": "philomena123", - "role": "moderator" - }, - { - "name": "Hoping For a Promotion", - "email": "assistant@example.com", - "password": "philomena123", - "role": "assistant" - }, - { - "name": "Pleb", - "email": "user@example.com", - "password": "philomena123", - "role": "user" - } - ], - "remote_images": [ - { - "url": "https://furrycdn.org/img/view/2020/9/30/35230.jpg", - "sources": [ - "https://twitter.com/Lou_Art_93/status/1311435253026828288" - ], - "tag_input": "safe, otter, clothes, female, solo, smiling, solo female, dialogue, shirt, pants, text, signature, semi-anthro, offscreen character, green eyes, happy, standing, cute, outdoors, disney, english text, talking, zootopia, featured image, mammal, fur, door, barefoot, mrs. otterton (zootopia), brown fur, mustelid, silhouette, japanese text, brown body, artist:louart, topwear, bottomwear" - }, - { - "url": "https://derpicdn.net/img/view/2019/3/26/1995489.webm", - "sources": [ - "https://twitter.com/StormXF3/status/1110609781897814023" - ], - "tag_input": "webm, breaking the fourth wall, oc:echo, visual effects of awesome, wide eyes, weapons-grade cute, featured image, oc only, irl human, ear fluff, artist:stormxf3, dilated pupils, laptop computer, slit eyes, leaning, behaving like a cat, eyes on the prize, solo focus, sound, safe, pov, pony, photo, daaaaaaaaaaaw, fangs, female, food, amazing, eye dilation, fourth wall, that bat pony sure does love fruits, frown, hand, hnnng, human, irl, male, mare, monitor, oc, computer, bat pony, apple, animated, cute, tracking, bat pony oc, cuteness overload, ear tufts, offscreen character, looking at something, ocbetes" - }, - { - "url": "https://furrycdn.org/img/view/2020/5/2/823.webm", - "sources": [ - "https://twitter.com/RikoSakari/status/1241720594107756544" - ], - "tag_input": "safe, clothes, tail, smiling, fangs, animated, feral, paws, sitting, wings, scarf, eyes closed, open mouth, semi-anthro, whiskers, duo, oc, happy, underpaw, dancing, cute, ambiguous gender, 2020, :3, watermark, webm, horns, paw pads, hug, ferret, featured image, mammal, sound, bat wings, artist:rikosakari, buttercup (song), music, hurondance, dot eyes, webbed wings, mustelid, domestic ferret, frame by frame, uwu, oc:riko sakari, smooth as butter, jack stauber" - }, - { - "url": "https://derpicdn.net/img/view/2018/3/10/1676327.jpg", - "sources": [ - "https://www.deviantart.com/jowyb/art/Strong-Petals-734821216" - ], - "tag_input": "younger, petals, featured image, sweet dreams fuel, jackabetes, weapons-grade cute, filly applejack, color porn, flower petals, smiling, wholesome, pear butter, pearabetes, jowybean is trying to murder us, artist:jowybean, duo, bed, bright, cute, daaaaaaaaaaaw, earth pony, eyes closed, feels, female, filly, freckles, happy, heartwarming, hnnng, hug, mare, morning ponies, mother and daughter, pillow, pony, precious, applejack, safe" - }, - { - "url": "https://derpicdn.net/img/view/2018/7/15/1781742.gif", - "sources": [ - "https://www.deviantart.com/szafir87/art/Shy-Hug-754638552" - ], - "tag_input": "szafir87 is trying to murder us, simple background, sitting, hiding behind wing, solo, transparent background, you are already dead, shyabetes, featured image, sweet dreams fuel, hug request, weapons-grade cute, eye shimmer, artist:szafir87, smiling, spread wings, adorable face, animated, blinking, blushing, bronybait, chest fluff, cute, daaaaaaaaaaaw, female, fluttershy, frown, gif, hiding, hnnng, looking at you, looking down, mare, mouth hold, note, pegasus, pony, raised hoof, safe, shy" - }, - { - "url": "https://furrycdn.org/img/view/2020/7/20/9844.jpg", - "sources": [ - "https://www.deviantart.com/tomatocoup/art/The-Guard-782523190" - ], - "tag_input": "safe, clothes, teeth, tail, female, solo, fangs, anthro, solo female, oc only, shark, breasts, cloud, swimsuit, looking at something, oc, standing, water, wet, fish, crepuscular rays, fish tail, one-piece swimsuit, ocean, fins, cloudy, shark tail, lifeguard, oc:erika (ambris), artist:tomatocoup" - }, - { - "url": "https://furrycdn.org/img/view/2020/4/25/180.jpg", - "sources": [ - "https://www.deviantart.com/kaleido-art/art/Dance-with-me-838409320" - ], - "tag_input": "safe, clothes, tail, female, smiling, ear fluff, anthro, male, wolf, dress, pants, eyes closed, simple background, open mouth, duo, canine, happy, shoes, artist:kaleido-art, haru (beastars), beastars, rabbit, dancing, cheek fluff, tan background, fluff, size difference, shipping, 2020, blush sticker, shadow, legoshi (beastars), featured image, plantigrade anthro, long ears, mammal, fur, white fur, male/female, lagomorph, gray fur, white body, ambient wildlife, gray body, ambient insect, harushi (beastars), anthro/anthro, bottomwear" - }, - { - "url": "https://derpicdn.net/img/view/2015/9/23/985817.gif", - "sources": [ - "http://duocartoonist.tumblr.com/post/129677819320/see-up-my-first-rendition-of-nmm-the-nebulous" - ], - "tag_input": "spread wings, alicorn, animated, artist:anima-dos, artist:lionheartcartoon, bat pony, bat wings, bedroom eyes, castle, crown, cute, evil laugh, eyeshadow, fangs, female, flapping, gif, grin, laughing, looking at you, makeup, mare, nightmare moon, open mouth, pony, raised hoof, redesign, safe, smirk, solo, unshorn fetlocks, slit eyes, artist:duo cartoonist, raised eyebrow, ethereal mane, moonbat, the moon rises, smiling, moonabetes, bat pony alicorn, smooth as butter" - }, - { - "url": "https://furrycdn.org/img/view/2020/4/30/672.jpg", - "sources": [ - "https://twitter.com/k_b__m/status/729172237761150978" - ], - "tag_input": "safe, clothes, tail, female, fluffy, anthro, male, dialogue, paws, white background, fox, simple background, open mouth, duo, canine, green eyes, claws, underpaw, rabbit, fluff, nick wilde (zootopia), disney, colored pupils, talking, paw pads, judy hopps (zootopia), zootopia, red fox, necktie, purple eyes, featured image, mammal, artist:k_b__m, 2016, lagomorph, fluffy tail, palm pads" - }, - { - "url": "https://derpicdn.net/img/view/2015/10/3/993821.jpg", - "sources": [ - "http://cannibalus.deviantart.com/art/Sunbutt-Portrait-563994112" - ], - "tag_input": "draw me like one of your french girls, drawing, duo, eating, fat, female, food, funny, funny as hell, ice cream, levitation, magic, obese, open mouth, painting, pony, prank, princess celestia, princess luna, prone, safe, smirk, tea, teacup, teapot, telekinesis, trolluna, wallpaper, this will end in pain, goblet, :t, tongue out, featured image, caricature, duo female, this will end in tears and/or a journey to the moon, best sisters, this will not end well, artception, smiling, ethereal tail, sibling rivalry, ethereal mane, royal sisters, nailed it, chubbylestia, lidded eyes, alicorn, cutelestia, lunabetes, sweet dreams fuel, technically advanced, tabun art-battle, tabun art-battle cover, artist:cannibalus, cake, cakelestia, close enough, cute" - }, - { - "url": "https://derpicdn.net/img/view/2017/10/27/1571166.gif", - "sources": [ - "https://therealdjthed.deviantart.com/art/Super-Smile-Animation-Test-711915586" - ], - "tag_input": "featured image, 3d, 3d model, animated, blender, blinking, bouncing, cute, cycles, earth pony, female, gif, grin, happy, headbob, mare, pinkie pie, pony, safe, simple background, solo, squee, diapinkes, weapons-grade cute, perfect loop, patreon, smiling, idle animation, ponk, patreon logo, cycles render, artist:therealdjthed, therealdjthed is trying to murder us, model:djthed, breathing" - }, - { - "url": "https://derpicdn.net/img/view/2020/8/10/2419740.gif", - "sources": [ - "https://2snacks.tumblr.com/post/626019622654787584/httpsyoutubezhu1luqmn9g" - ], - "tag_input": "get stick bugged lol, princess cadance, featured image, adorawat, perfect loop, pony, pixel art, meme, female, dancing, changeling queen, changeling, artist:2snacks, animated, alicorn, wat, synchronized, sweatdrop, sweat, open mouth, cute, queen chrysalis, safe, crystal castle, princess flurry heart, ponified meme, blursed image" - }, - { - "url": "https://furrycdn.org/img/view/2021/5/22/87948.png", - "sources": [ - "https://www.deviantart.com/yakovlev-vad/art/Sisu-878339374" - ], - "tag_input": "safe, tail, female, solo, smiling, solo female, hair, feral, paws, dragon, tree, claws, eyebrows, water, disney, horns, eyelashes, dragoness, featured image, leaf, mane, side view, eastern dragon, magenta eyes, pink eyes, purple hair, blue hair, blue body, aquatic dragon, holding object, artist:yakovlev-vad, blue mane, fictional species, artifact, multicolored body, 2021, raya and the last dragon, sisu (raya and the last dragon), water bubble" - }, - { - "url": "https://furrycdn.org/img/view/2020/7/11/7370.png", - "sources": [ - "https://twitter.com/HiccupsDoesSFW/status/959798227993317376/photo/1" - ], - "tag_input": "safe, clothes, tail, female, solo, smiling, ear fluff, anthro, solo female, hair, paws, looking at you, sitting, my little pony, wolf, collar, abstract background, signature, crossed legs, canine, underpaw, fluff, species swap, paw pads, digitigrade anthro, featured image, mammal, hasbro, white outline, sunset shimmer (mlp), artist:hiccupsdoesart, friendship is magic" - }, - { - "url": "https://derpicdn.net/img/view/2016/6/20/1182765.gif", - "sources": [ - "http://megamanhxh.deviantart.com/art/Animation-A-malfunctioning-book-pony-616553371" - ], - "tag_input": "silly pony, simple background, sitting, solo, twilight sparkle, white background, wingboner, tongue out, dork, discussion in the comments, twiabetes, weapons-grade cute, majestic as fuck, behaving like a dog, lidded eyes, active stretch, smiling, artist:megamanhxh, spread wings, twilight sparkle (alicorn), safe, pony, photoshop, gif, floppy ears, flexible, flapping, ear scratch, daaaaaaaaaaaw, cute, animated, alicorn, adorkable, :p, silly, scratching, female, show accurate" - }, - { - "url": "https://furrycdn.org/img/view/2020/7/8/6299.png", - "sources": [ - "" - ], - "tag_input": "safe, tail, solo, ear fluff, tongue out, feral, paws, fox, simple background, transparent background, one eye closed, canine, claws, cheek fluff, fluff, cute, ambiguous gender, yellow eyes, holding, head fluff, colored pupils, blep, tongue, furbooru exclusive, featured image, mammal, high res, furbooru, white outline, tail hold, tail fluff, artist:chonkycrunchy, astra" - }, - { - "url": "https://derpicdn.net/img/view/2015/3/12/847656.gif", - "sources": [ - "http://nuksponyart.tumblr.com/post/113386426665/young-twilight-understands-every-cat-owners" - ], - "tag_input": "behaving like a cat, looking up, curled up, spikelove, twiabetes, featured image, bookshelf, weapons-grade cute, wide eyes, spikabetes, artist:nukilik, lidded eyes, definition of insanity, smiling, unicorn twilight, nukilik is trying to murder us, filly twilight sparkle, that pony sure does love books, frame by frame, debate in the comments, animated, annoyed, baby, baby dragon, baby spike, book, cute, cutie mark, daaaaaaaaaaaw, diabetes, dragon, eyeroll, eyes closed, female, filly, floppy ears, frown, grumpy, hnnng, ladder, levitation, library, loop, magic, male, mama twilight, nuzzling, photoshop, pony, ponyloaf, prone, reading, safe, sitting, sleeping, snuggling, spike, telekinesis, twilight sparkle, unamused, unicorn, yawn, younger" - }, - { - "url": "https://derpicdn.net/img/view/2014/10/9/739465.jpg", - "sources": [ - "https://devinian.deviantart.com/art/The-Golden-Cage-487369223" - ], - "tag_input": "politics in the comments, alicorn, artist:devinian, beautiful, cage, cake, chest, clothes, couch, crepuscular rays, cushion, derail in the comments, detailed, dress, female, fireplace, fishnets, interior, jewelry, levitation, light, magic, mare, painting, palace, philomena, photoshop, pony, princess celestia, princess luna, safe, scenery, socks, phoenix, wall of tags, technically advanced, duo, pegasus, rainbow dash, luxury, absurd resolution, tea, teacup, teapot, telekinesis, twilight sparkle, wallpaper, window, indoors, baroque, bird cage, glorious, scenery porn, featured image, chandelier, dust motes, color porn, smiling, new crown, twilight sparkle (alicorn)" - }, - { - "url": "https://furrycdn.org/img/view/2020/8/26/20869.png", - "sources": [ - "" - ], - "tag_input": "safe, tail, solo, ear fluff, anthro, feral, paws, fox, sitting, chest fluff, speech bubble, text, simple background, signature, canine, cheek fluff, fluff, cute, meme, tears, talking, purple background, furbooru exclusive, featured image, chibi, mammal, high res, fur, furbooru, front view, purple fur, artist:sorajona, artist:skodart, astra, astrael, mascot, joke, tempting fate, bottom, this will end in lewds" - }, - { - "url": "https://furrycdn.org/img/view/2021/3/17/74641.jpg", - "sources": [ - "https://twitter.com/popodunk/status/1370774397024342016" - ], - "tag_input": "safe, clothes, teeth, female, solo, smiling, anthro, solo female, looking at you, white background, dress, blushing, simple background, hat, rabbit, cute, eyebrows, flower, disney, judy hopps (zootopia), zootopia, purple eyes, featured image, floppy ears, mammal, fur, lagomorph, gray fur, sun hat, gray body, smiling at you, artist:popodunk" - }, - { - "url": "https://derpicdn.net/img/view/2017/3/4/1379251.jpg", - "sources": [ - "http://viwrastupr.deviantart.com/art/My-Little-Pony-Friendship-is-Magic-667085058" - ], - "tag_input": "trixie's wagon, alicorn, alicorn amulet, angel bunny, apple, apple bloom, applejack, artist:viwrastupr, bag, balcony, beautiful, big macintosh, blaze, blossomforth, book, bow, bridge, canterlot, canyon, cape, caramel, castle, cello, changeling, cloak, clothes, cloud, cloud kicker, cloudchaser, cloudsdale, clubhouse, crib, crown, crusaders clubhouse, crystal ball, crystal empire, cup, cute, cutie mark crusaders, daily deviation, daisy, derpy hooves, discord, dj pon-3, doctor whooves, dragon, drink, element of magic, epic, everfree forest, eyes closed, fancypants, fascinating, female, fleetfoot, fleur-de-lis, flitter, flower, flower trio, flower wishes, fluttershy, flying, food, friends, fruit, gilda, glass, glasses, glowing horn, goggles, grass, griffon, group, gummy, hair bow, hat, jewelry, lake, lemon hearts, light, lily, lyra heartstrings, magic, male, mane seven, mane six, minuette, moondancer, mountain, mountain range, mouth hold, multicolored hair, necktie, night, oc, opalescence, open mouth, owlowiscious, park, path, pegasus, philomena, phoenix, pillow, pinkie pie, playing, poison joke, pond, pony, ponyville, potion, princess cadance, princess celestia, princess luna, queen chrysalis, rainbow dash, raised hoof, rarity, raven, reading, river, rose, roseluck, safe, salad, scarf, scenery, school, scootaloo, self ponidox, shining armor, sitting, sky, sleeping, soarin', solar system, spike, spitfire, stage, starry night, stars, statue, sunburst, surprise, surprised, swarm, sweetie belle, sweetie drops, table, tank, telekinesis, tiara, timber wolf, time turner, toffee, tree, trixie, trixie's cape, twilight sparkle, twinkleshine, uniform, vinyl scratch, water, waterfall, wings, winona, wonderbolts, wonderbolts uniform, zebra, zecora, zecora's hut, looking at each other, bon bon, regalia, canterlot castle, pulling, father and daughter, scenery porn, octavia melody, balancing, lily valley, featured image, underhoof, bookshelf, trixie's hat, night sky, greta, castle of the royal pony sisters, large wings, twilight's castle, cello bow, lidded eyes, the hall of friendship, backwards cutie mark, alicorn hexarchy, color porn, starlight glimmer, cutie map, too big for derpibooru, griffonstone, castle griffonstone, smiling, tantabus, spread wings, gabby, tree of harmony, majestic, curved horn, oc:fausticorn, canterlot five, princess flurry heart, wall of tags, multicolored tail, colored pupils, bow (instrument), magnum opus, astronomical detail, twilight sparkle (alicorn), musical instrument, absurd resolution, technically advanced, sweet apple acres" - }, - { - "url": "https://derpicdn.net/img/view/2013/2/3/232093.gif", - "sources": [ - "http://www.reddit.com/r/mylittlepony/comments/17s485/did_someone_ask_for_a_gif_or_apng_of_dash_and/" - ], - "tag_input": "dashabetes, shifty eyes, artist:marminatoror, just for sidekicks, nose rub, transparent background, sleepless in ponyville, simple background, season 3, filly, pegasus, adventure in the comments, scootalove, safe, upvote event horizon, smiling, tsunderainbow, nose kiss, animated, scootaloo, boop, cute, cutealoo, daaaaaaaaaaaw, duo, derail in the comments, edit, eyes closed, female, gif, grin, happy, hnnng, looking around, mare, nuzzling, pony, rainbow dash, saddle bag, weapons-grade cute, tsundere, sweet dreams fuel" - }, - { - "url": "https://furrycdn.org/img/view/2020/7/5/5709.png", - "sources": [ - "https://twitter.com/2d10art/status/1279807434102628352/photo/1" - ], - "tag_input": "safe, clothes, tail, female, solo, ear fluff, anthro, solo female, paws, looking at you, signature, hat, pokémon, underpaw, cheek fluff, fluff, braixen, forest, outdoors, head fluff, raised tail, nintendo, digitigrade anthro, featured image, leaf, amber eyes, kneeling, yellow fur, mammal, fur, front view, tail fluff, crouching, white fur, shoulder fluff, orange fur, artist:2d10art, fictional species, technical advanced, starter pokémon" - }, - { - "url": "https://derpicdn.net/img/view/2014/3/20/580031.gif", - "sources": [ - "http://misterdavey.tumblr.com/post/80134333183/lesbian-fantasies" - ], - "tag_input": "gala dress, clothes, candle, bubble, blushing, bipedal, bedroom eyes, pegasus, unicorn, eyes closed, fantasizing, discussion in the comments, female, flower, food, frame, crush, hnnng, hug, imagination, juice, diabetes, juice box, kissing, lesbian, nightgown, nose wrinkle, nuzzling, out of character, picture, picture frame, plate, artist:misterdavey, animated, adventure in the comments, plushie, pony, rainbow dash, rainbow dash always dresses in style, rainbow dash's house, raridash, rarity, refrigerator, rose, safe, see-through, shipping, snuggling, spoon, standing, table, waifu dinner, not creepy, plates, spiderman thread, eye contact, dashabetes, featured image, weapons-grade cute, raribetes, jontron thread, eye shimmer, doctor who thread, waltz, rarity plushie, smiling, spread wings, wall of tags, crush plush, misterdavey is trying to murder us, jontron in the comments, doctor who in the comments, spiderman in the comments, dinner, doll, dress, derail in the comments, useless source url, source needed, dead source, dancing, daaaaaaaaaaaw, cute, cuddling" - }, - { - "url": "https://derpicdn.net/img/view/2015/4/20/878570.gif", - "sources": [ - "http://supereddit-blog.tumblr.com/post/116885770608/sweetie-belle-gets-her-cutiemark" - ], - "tag_input": "special talent, sweetie belle, telekinesis, vinyl scratch, wat, gritted teeth, diamondbelle, artist:superedit, the great and powerful superedit, octavia melody, wide eyes, unexpected, cutiespark, discovery family logo, bloom and gloom, music judges meme, smiling, hoof hold, what the hay?, funny, grin, implied shipping, frown, judges, lesbian, magic, season 5, gif, earth pony, unicorn, animated, bedroom eyes, bipedal, blushing, cute, cutie mark, diamond tiara, dj pon-3, edit, edited screencap, embarrassed, facehoof, female, meme, open mouth, photoshop, pony, rarity, safe, score, screencap, shipping" - }, - { - "url": "https://furrycdn.org/img/view/2020/4/20/0.png", - "sources": [ - "" - ], - "tag_input": "safe, tail, solo, fluffy, fox, eyes closed, simple background, transparent background, canine, vector, cheek fluff, fluff, ambiguous gender, vulpine, head fluff, furbooru exclusive, .svg available, featured image, meta, mammal, svg, fur, ambiguous form, furbooru, logo, artist:aureai, digital art, purple fur, fluffy tail, astra, mascot, it begins" - }, - { - "url": "https://derpicdn.net/img/view/2015/1/3/798402.gif", - "sources": [ - "http://nuksponyart.tumblr.com/post/106937983375/shining-giving-his-litle-sister-a-pony-version-of" - ], - "tag_input": "sweet dreams fuel, weapons-grade cute, brother and sister, twilight sparkle, duckery in the comments, artist:nukilik, sibling bonding, equestria's best brother, smiling, shining adorable, nukilik is trying to murder us, sweet, siblings, shining armor, safe, pony, ponies riding ponies, piggyback ride, photoshop, open mouth, hnnng, heartwarming, happy, grin, filly, diabetes, daaaaaaaaaaaw, cute, bouncing, bbbff, animated, duo, younger, filly twilight sparkle, female, gif, twilight riding shining armor, riding, unicorn, unicorn twilight, twiabetes, frame by frame, featured image, equestria's best big brother" - }, - { - "url": "https://furrycdn.org/img/view/2020/7/10/7015.png", - "sources": [ - "https://www.deviantart.com/yakovlev-vad/art/Faun-Elora-762586083" - ], - "tag_input": "safe, tail, female, solo, pubic fluff, smiling, ear fluff, anthro, solo female, breasts, jewelry, chest fluff, cleavage, tree, green eyes, fluff, cute, bracelet, outdoors, spyro the dragon (series), eyelashes, mammal, sexy, fur, faun, tail fluff, shoulderless, shoulder fluff, orange fur, artist:yakovlev-vad, elora (spyro), adorasexy, minidress, strapless, fictional species" - }, - { - "url": "https://furrycdn.org/img/view/2020/5/6/1252.jpg", - "sources": [ - "https://www.deviantart.com/tamberella/art/Eevee-s-Rainbow-790535422" - ], - "tag_input": "safe, tail, feral, paws, eevee, reflection, grass, eeveelution, vaporeon, pokémon, sylveon, jolteon, neck fluff, flareon, glaceon, espeon, leafeon, fluff, umbreon, fire, ambiguous gender, group, artist:tamberella, nintendo, fish tail, running, featured image, leaf, 2019, yellow fur, mammal, blue fur, fur, fins, digital art, brown fur, pink fur, black fur, purple fur, green fur, orange fur, fictional species, color porn, technical advanced" - }, - { - "url": "https://derpicdn.net/img/view/2016/5/6/1147843.gif", - "sources": [ - "http://trombonyponypie.deviantart.com/art/Wakey-Wakey-Animated-Gif-607274893" - ], - "tag_input": "gif, artist:trombonyponypie, smiling, visual effects of awesome, weapons-grade cute, underhoof, diapinkes, yawn, waking up, stretching, solo, sleeping, safe, realistic, pony, pinkie pie, pillow, morning ponies, looking at you, hnnng, happy, fluffy, eyes closed, earth pony, diabetes, derail in the comments, cute, 3d, c:, blender, eyes open, smiling at you, on side, female, blanket, sweet dreams fuel, bed, animated, adventure in the comments, detailed hair" - }, - { - "url": "https://furrycdn.org/img/view/2021/3/23/76073.png", - "sources": [ - "https://twitter.com/wolfypon/status/1374492788742496261" - ], - "tag_input": "safe, tail, female, solo, ear fluff, solo female, oc only, feral, paws, fox, chest fluff, simple background, transparent background, oc, canine, commission, cheek fluff, neck fluff, fluff, eyebrows, head fluff, eyelashes, featured image, mammal, blue fur, cyan eyes, butt fluff, high res, fur, tail fluff, shoulder fluff, artist:wolfypon, socks (leg marking), blue body, multicolored fur, firefox (browser), vixen, globe, 2021, oc:double colon" - }, - { - "url": "https://derpicdn.net/img/view/2015/3/27/858027.gif", - "sources": [ - "https://derpibooru.org/images/858027" - ], - "tag_input": "artist:sampodre, gif, creepy awesome, silhouette, 3d, adventure in the comments, animated, changeling, cinemagraph, epic, female, gif party, glowing eyes, open mouth, photoshop, queen chrysalis, rain, safe, sharp teeth, smirk, solo, wet, featured image, visual effects of awesome, derpibooru exclusive" - }, - { - "url": "https://derpicdn.net/img/view/2015/11/21/1026784.gif", - "sources": [ - "https://twitter.com/ziroro326/status/667726177813958656" - ], - "tag_input": "missing cutie mark, adorkable, alicorn, animated, black and white, cute, dancing, derp, ear twitch, female, floppy ears, grayscale, kicking, mare, monochrome, party hard, pixiv, pony, safe, silly, silly pony, simple background, solo, swing, twilight sparkle, white background, dork, :o, twiabetes, sweet dreams fuel, weapons-grade cute, artist:jirousan, do the sparkle, twilight sparkle (alicorn), jirousan is trying to murder us, club can't handle me, frame by frame" - }, - { - "url": "https://furrycdn.org/img/view/2020/7/23/11106.gif", - "sources": [ - "https://ostinlein.tumblr.com/post/617228745665839104/characters-belong" - ], - "tag_input": "safe, clothes, teeth, tail, fangs, anthro, male, oc only, piercing, animated, sitting, dog, shirt, pants, kissing, bird, signature, looking at each other, hat, duo, oc, gif, canine, claws, cheek fluff, neck fluff, fluff, cute, beak, scenery, holding, head fluff, shipping, hand hold, drink, digitigrade anthro, indoors, necktie, feathers, featured image, sharp teeth, mammal, hand on face, fur, tail fluff, glass, wine glass, brown fur, bird feet, males only, male/male, frame by frame, bar, tail feathers, oc x oc, red feathers, yellow feathers, tan fur, suspenders, artist:ostinlein, oc:tyler, oc:fletcher, galliform, art deco, topwear, smooth as butter, bottomwear" - }, - { - "url": "https://furrycdn.org/img/view/2020/8/29/22679.gif", - "sources": [ - "https://www.furaffinity.net/view/25506150/" - ], - "tag_input": "safe, clothes, solo, anthro, male, animated, paws, solo male, goat, abstract background, signature, open mouth, gif, fluff, cute, undertale, asriel dreemurr (undertale), flower, head fluff, tongue, featured image, floppy ears, black eyes, mammal, bovid, frame by frame, sneezing, artist:absolutedream" - }, - { - "url": "https://furrycdn.org/img/view/2020/4/25/267.gif", - "sources": [ - "https://inkbunny.net/s/1766018" - ], - "tag_input": "safe, clothes, female, solo, anthro, solo female, deer, oc only, breasts, freckles, animated, eyes closed, skirt, oc, gif, happy, dancing, cute, artist:kanashiipanda, book of lust, oc:julia woods, mammal, frame by frame, ocbetes, smooth as butter, bottomwear" - }, - { - "url": "https://derpicdn.net/img/view/2017/3/10/1383501.gif", - "sources": [ - "http://deannart.deviantart.com/art/Preview-2-Slice-Of-Life-668121278" - ], - "tag_input": "smooth as butter, glare, hilarious in hindsight, magic, open mouth, pony, frown, eyes closed, earth pony, drool, luna is not amused, frame by frame, alicorn, animated, marriage, artist:deannart, blinking, fiery shimmer, cutelestia, cute, mare, female, nintendo, featured image, wedding, bored, preview, princess celestia, princess luna, safe, sigh, sitting, sleeping, snoring, unamused, unicorn, gritted teeth, sunset shimmer, underhoof, maud pie, majestic as fuck, gamer sunset, lidded eyes, gif, slice of life (episode), smiling, hoof hold, nintendo switch" - }, - { - "url": "https://furrycdn.org/img/view/2020/8/19/18670.jpg", - "sources": [ - "https://twitter.com/Ruribec/status/1190149320374284289" - ], - "tag_input": "safe, clothes, teeth, female, solo, smiling, fangs, anthro, solo female, hair, oc only, paws, looking at you, sky, dress, cat, tree, signature, oc, claws, grass, underpaw, cheek fluff, standing, fluff, feline, yellow eyes, raised leg, outdoors, night, food, paw pads, digitigrade anthro, featured image, sharp teeth, mammal, blue fur, ear tuft, fur, front view, slit pupils, barefoot, fence, halloween, white fur, holiday, gray fur, gray hair, white body, graveyard, pumpkin, gray body, vegetables, artist:ruribec, oc:kelly (ruribec), cemetery" - }, - { - "url": "https://derpicdn.net/img/view/2015/3/22/854962.gif", - "sources": [ - "http://joshng.deviantart.com/art/Rainbow-Rockin-Animation-521841679" - ], - "tag_input": "ponytail, human coloration, musical instrument, alternate hairstyle, animated, awesome, boots, clothes, decepticon, equestria girls, eyes closed, female, guitar, headbang, human, humanized, jacket, kneesocks, metal, rainbow dash, rainbow socks, safe, shirt, shorts, simple background, skinny, smirk, socks, solo, transformers, white background, denim, electric guitar, striped socks, perfect loop, full body, artist:joshng, smiling, smooth as butter" - }, - { - "url": "https://furrycdn.org/img/view/2020/10/28/42897.png", - "sources": [ - "https://www.furaffinity.net/view/33574531/" - ], - "tag_input": "safe, clothes, tail, female, solo, anthro, solo female, hair, breasts, bra, panties, underwear, cell phone, phone, green eyes, rabbit, cute, angry, colored pupils, eyelashes, indoors, plushie, long hair, featured image, long ears, kneeling, mammal, tank top, sexy, fur, front view, short tail, brown hair, brown fur, all fours, pale belly, small breasts, gloves (arm marking), socks (leg marking), lagomorph, furniture, multicolored fur, game controller, tan fur, frame, adorasexy, frowning, brown body, tan body, madorable, topwear, pouting, artist:autumndeer" - }, - { - "url": "https://furrycdn.org/img/view/2021/1/18/62645.jpg", - "sources": [ - "https://twitter.com/ActuallyYshanii/status/1350865352243224581" - ], - "tag_input": "safe, clothes, tail, female, solo, anthro, solo female, paws, fox, thigh highs, heart, snow, canine, cheek fluff, fluff, cute, raised tail, blue eyes, leg warmers, face down ass up, digitigrade anthro, featured image, mammal, legwear, fur, tail wag, front view, three-quarter view, toeless legwear, orange fur, snowfall, brown nose, cream fur, orange body, vixen, cream body, artist:yshanii" - }, - { - "url": "https://furrycdn.org/img/view/2020/6/21/3578.png", - "sources": [ - "https://www.furaffinity.net/view/36893257/" - ], - "tag_input": "safe, tail, female, solo, smiling, solo female, hair, feral, paws, cloud, eyes closed, sleeping, lying down, underpaw, fluff, scenery, goggles, hammer, prone, scenery porn, featured image, blue fur, fur, front view, tail fluff, ringtail, ratchet & clank, lombax, feralized, prosthetics, artist:viwrastupr, white hair, prosthetic arm, fictional species, technical advanced, rivet (r&c)" - }, - { - "url": "https://derpicdn.net/img/view/2017/3/6/1381041.gif", - "sources": [ - "http://luminaura.deviantart.com/art/Rubbing-all-the-princesses-cheeks-656801151" - ], - "tag_input": "twiabetes, sweet dreams fuel, weapons-grade cute, wide eyes, lunabetes, patreon, alicorn tetrarchy, artist:lumineko, lidded eyes, lumineko is trying to murder us, smiling, spread wings, cutedance, patreon logo, lumineko's nuzzling princesses, twilight sparkle (alicorn), non-consensual nuzzling, luna is not amused, daaaaaaaaaaaw, animated, blushing, c:, crown, cute, cutelestia, cheek to cheek, eyes closed, alicorn, female, floppy ears, flower, frown, gif, hape, hnnng, hug, jewelry, mare, nuzzling, open mouth, pony, princess cadance, princess celestia, princess luna, rubbing, safe, snuggling, surprised, twilight sparkle, varying degrees of want, wink, regalia, :t, :o, one eye closed" - }, - { - "url": "https://furrycdn.org/img/view/2021/3/19/75280.gif", - "sources": [ - "https://www.furaffinity.net/view/32400757/" - ], - "tag_input": "safe, female, solo, smiling, solo female, hair, oc only, tongue out, animated, feral, white background, eyes closed, blushing, simple background, open mouth, oc, gif, cheek fluff, fluff, feline, cute, shy, lynx, blep, feather, tongue, featured image, 2019, mammal, teal eyes, ear tuft, fur, low res, pale belly, frame by frame, spotted fur, artist:tuwka, feather in hair, solo ambiguous, hair accessory, oc:kamari" - }, - { - "url": "https://furrycdn.org/img/view/2020/8/8/15561.jpg", - "sources": [ - "https://www.furaffinity.net/view/31801339/" - ], - "tag_input": "safe, tail, female, solo, smiling, ear fluff, anthro, solo female, hair, oc only, butt, looking at you, fox, bird, abstract background, signature, oc, canine, fluff, toy, cute, bathtub, water, wet, raised tail, blue eyes, duck, bathroom, indoors, featured image, floppy ears, mammal, sexy, ears, fur, tail fluff, the ass was fat, bubbles, orange hair, 2018, orange fur, bath, >:3, mischievous, adorasexy, cream fur, orange body, vixen, artist:amarihel, bubble bath, rubber duck, oc:tfs (amarihel), waterfowl, cream body" - }, - { - "url": "https://derpicdn.net/img/view/2019/1/16/1937202.gif", - "sources": [ - "" - ], - "tag_input": "animated, bedroom eyes, blinking, blowing, derpibooru, dialogue, downvote, earth pony, everything is ruined, female, flash, floppy ears, frown, gif, glowing horn, green eyes, happy, levitation, looking back, looking down, magic, mare, meta, metamorphosis, now you fucked up, oc, open mouth, ponified, pony, red eyes, sad, safe, simple background, sisters, telekinesis, transformation, unicorn, upvote, white background, wink, looking at each other, looking up, one eye closed, oc only, featured image, wide eyes, derpibooru exclusive, smiling, artist:justisanimation, oc:upvote, oc:downvote, derpibooru ponified, downvote's downvotes, shook, this will end in pain, ear twitch, shocked, horrified, shrunken pupils, wall of tags" - }, - { - "url": "https://derpicdn.net/img/view/2014/8/18/702641.png", - "sources": [ - "http://ukulilia.deviantart.com/art/Coffee-to-stay-awake-all-night-475492803" - ], - "tag_input": "smug, safe, realistic, princess luna, artist:mykegreywolf, pony, mug, technically advanced, makeup, magic, female, looking at you, translated in the comments, alternate hairstyle, lips, high res, german, eyeshadow, detailed, cute, alicorn, artist:katputze, collaboration, coffee mug, coffee, beautiful, photoshop elements, smiling, lidded eyes, lunabetes, featured image, 2014, telekinesis, solo" - }, - { - "url": "https://derpicdn.net/img/view/2019/5/10/2035594.gif", - "sources": [ - "https://twitter.com/nastylittlepest/status/1126899230151467008" - ], - "tag_input": "sweet dreams fuel, frame by frame, ocbetes, lidded eyes, smiling, artist:angrylittlerodent, oc:pezzhorse, wholesome, rodent is trying to murder us, oc:rodentmod, snuggling, sleeping, safe, precious, pony, pillow, oc, nuzzling, mare, male, hug, hnnng, gif, frown, floppy ears, female, eyes closed, earth pony, ear twitch, dark, daaaaaaaaaaaw, cute, cuddling, couple, blanket, dead source, bed, duo, animated, :<, stallion, unicorn, ear fluff, oc only, featured image, weapons-grade cute" - }, - { - "url": "https://derpicdn.net/img/view/2012/1/2/0.jpg", - "sources": [ - "" - ], - "tag_input": "chair, eyes, female, grin, hilarious in hindsight, adventure in the comments, image macro, derpibooru legacy, cigar, building, smiling, featured image, gritted teeth, swinging, stallion, spider-man, smoking, sitting, safe, pony, ponified, phone, pegasus, parody, paper, necktie, muffin, meme, mare, male, mail, letter, j. jonah jameson, it begins, derpy hooves, bag, song in the comments, artist needed" - }, - { - "url": "https://derpicdn.net/img/view/2015/9/26/988000.gif", - "sources": [ - "https://derpibooru.org/988000" - ], - "description": "Fairly large GIF (~23MB), use to test WebM stuff.", - "tag_input": "alicorn, angry, animated, art, artist:assasinmonkey, artist:equum_amici, badass, barrier, crying, dark, epic, female, fight, force field, glare, glow, good vs evil, lord tirek, low angle, magic, mare, messy mane, metal as fuck, perspective, plot, pony, raised hoof, safe, size difference, spread wings, stomping, twilight's kingdom, twilight sparkle, twilight sparkle (alicorn), twilight vs tirek, underhoof" - }, - { - "url": "https://derpicdn.net/img/2012/1/2/25/large.png", - "sources": [ - "https://derpibooru.org/25" - ], - "tag_input": "artist:moe, canterlot, castle, cliff, cloud, detailed background, fog, forest, grass, mountain, mountain range, nature, no pony, outdoors, path, river, safe, scenery, scenery porn, signature, source needed, sunset, technical advanced, town, tree, useless source url, water, waterfall, widescreen, wood" - }, - { - "url": "https://derpicdn.net/img/2018/6/28/1767886/full.webm", - "sources": [ - "http://hydrusbeta.deviantart.com/art/Gleaming-in-the-Sun-Our-Colors-Shine-in-Every-Hue-611497309" - ], - "tag_input": "3d, animated, architecture, artist:hydrusbeta, castle, cloud, crystal empire, crystal palace, flag, flag waving, no pony, no sound, safe, scenery, webm" - }, - { - "url": "https://derpicdn.net/img/view/2015/2/19/832750.jpg", - "sources": [ - "http://sovietrussianbrony.tumblr.com/post/111504505079/this-image-actually-took-me-ages-to-edit-the" - ], - "tag_input": "artist:rhads, artist:the sexy assistant, canterlot, cloud, cloudsdale, cloudy, edit, lens flare, no pony, ponyville, rainbow, river, safe, scenery, sweet apple acres" - }, - { - "url": "https://derpicdn.net/img/view/2016/3/17/1110529.jpg", - "sources": [ - "https://www.deviantart.com/devinian/art/Commission-Crystals-of-thy-heart-511134926" - ], - "tag_input": "artist:devinian, aurora crystialis, bridge, cloud, crepuscular rays, crystal empire, crystal palace, edit, flower, forest, grass, log, mountain, no pony, river, road, safe, scenery, scenery porn, source needed, stars, sunset, swing, tree, wallpaper" - }, - { - "url": "https://derpicdn.net/img/view/2019/6/16/2067468.svg", - "sources": [ - "https://derpibooru.org/2067468" - ], - "tag_input": "artist:cheezedoodle96, babs seed, bloom and gloom, cutie mark, cutie mark only, no pony, safe, scissors, simple background, svg, .svg available, transparent background, vector" - } - ], - "comments": [ - "bold is **bold**, italic is _italic_, spoiler is ||spoiler||, code is `code`, underline is __underline__, strike is ~~strike~~, sup is ^sup^, sub is %sub%.", - "inline embedded thumbnails (tsp): >>1t >>1s >>1p", - "embedded image inside a spoiler: ||who needs it anyway >>1s||", - "spoilers inside of a table\n\nHello | World\n--- | ---:\n`||cool beans!||` | ||cool beans!||" - ], - "forum_posts": [{ - "forum": "dis", - "topics": [{ - "title": "Example Topic", - "posts": [ - "example post", - "yet another example post" - ] - }, - { - "title": "Second Example Topic", - "posts": [ - "post", - "post 2" - ] - } - ]}, - { - "forum": "art", - "topics": [{ - "title": "Embedded Images", - "posts": [ - ">>1t >>1s >>1p", - ">>1", - "non-existent: >>1000t >>1000s >>1000p >>1000" - ] - }] - } - ] -} From 2fc9a18f9fdb709a158819405a5e4accbd3502ab Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 12 Jun 2024 20:50:46 +0200 Subject: [PATCH 40/65] editor styling + media list styling --- assets/css/elements/block.css | 13 +++----- assets/css/elements/form.css | 4 +++ assets/css/elements/input.css | 14 ++++++-- assets/css/elements/media.css | 6 ++++ assets/css/options/compatibility.css | 2 +- assets/css/options/extra-interaction-info.css | 7 ++++ assets/css/themes/dark-blue.css | 3 ++ assets/css/themes/dark-purple.css | 3 ++ assets/css/themes/light-blue.css | 3 ++ assets/css/views/communication.css | 32 ++++++++++++------- assets/css/views/interaction.css | 12 +++++++ .../templates/admin/report/show.html.slime | 2 +- .../templates/communication/_body.html.slime | 3 +- .../conversation/message/_form.html.slime | 4 +-- .../templates/conversation/new.html.slime | 2 +- .../templates/image/_image_box.html.slime | 9 +++--- .../templates/image/comment/_form.html.slime | 2 +- .../templates/image/comment/edit.html.slime | 4 +-- .../image/description/_form.html.slime | 1 - .../templates/image/new.html.slime | 2 +- .../templates/markdown/_help.html.slime | 17 ---------- .../templates/markdown/_input.html.slime | 13 ++++---- .../templates/markdown/_toolbar.html.slime | 7 ++-- .../templates/message/_message.html.slime | 8 +---- .../profile/description/edit.html.slime | 2 +- .../profile/scratchpad/edit.html.slime | 2 +- .../templates/topic/new.html.slime | 4 +-- .../templates/topic/post/_form.html.slime | 2 +- .../templates/topic/post/edit.html.slime | 4 +-- lib/philomena_web/views/app_view.ex | 5 +++ priv/repo/seeds/pages/rules.md | 1 + 31 files changed, 113 insertions(+), 80 deletions(-) create mode 100644 assets/css/options/extra-interaction-info.css delete mode 100644 lib/philomena_web/templates/markdown/_help.html.slime diff --git a/assets/css/elements/block.css b/assets/css/elements/block.css index 3da040f92..ad373336a 100644 --- a/assets/css/elements/block.css +++ b/assets/css/elements/block.css @@ -59,8 +59,7 @@ border-bottom-right-radius: var(--border-radius-outer) !important; } -.block__content, -.block__tab { +.block__content { border-radius: var(--border-radius-outer); padding: var(--padding-normal); background: var(--primary-dark-color); @@ -146,14 +145,10 @@ margin-top: 0; } -.block__tab { - padding: var(--padding-normal); -} - .block__header--js-tabbed { display: flex; padding: 0; - gap: var(--padding-normal); + gap: var(--padding-small); } @mixin if-mobile { @@ -169,11 +164,11 @@ } .block__header--js-tabbed a:hover { - background: var(--background-color); + background: var(--primary-dark-color); } .block__header--js-tabbed .selected { - background: var(--background-color); + background: var(--primary-dark-color); } .block--spaced-top { diff --git a/assets/css/elements/form.css b/assets/css/elements/form.css index e992af50d..75bc940dc 100644 --- a/assets/css/elements/form.css +++ b/assets/css/elements/form.css @@ -4,6 +4,10 @@ form .field { margin-bottom: var(--padding-normal); } +form .field:last-child { + margin-bottom: 0; +} + form .form--two-column { display: grid; grid: inherit; diff --git a/assets/css/elements/input.css b/assets/css/elements/input.css index bd973879b..4e07ecabc 100644 --- a/assets/css/elements/input.css +++ b/assets/css/elements/input.css @@ -1,16 +1,17 @@ .input { - border: 1px solid var(--secondary-border-color); + border: 1px solid var(--invisible-color); + background: var(--primary-dark-color); border-radius: var(--border-radius-inner); - background: var(--secondary-dark-color); color: var(--text-color); padding: var(--padding-small); box-sizing: border-box; font-family: var(--font-family-monospace); + box-shadow: 0; } .input:hover, .input:focus { - background: var(--secondary-muted-color); + background: var(--secondary-dark-color); } .input:focus { @@ -31,6 +32,13 @@ cursor: text; } +textarea.input:hover, +textarea.input:focus, +.input--text:hover, +.input--text:focus { + border: 1px solid var(--secondary-border-color); +} + input[type="file"] { padding-top: var(--padding-small); } diff --git a/assets/css/elements/media.css b/assets/css/elements/media.css index bbc6b329c..748e38e31 100644 --- a/assets/css/elements/media.css +++ b/assets/css/elements/media.css @@ -165,3 +165,9 @@ .image-scaled { max-width: 100%; } + +@mixin if-phone { + .media-box__header--link-row { + grid-template-columns: 1fr 1fr 1.5fr 1fr 1fr; + } +} diff --git a/assets/css/options/compatibility.css b/assets/css/options/compatibility.css index 41b66fa17..ec276268e 100644 --- a/assets/css/options/compatibility.css +++ b/assets/css/options/compatibility.css @@ -33,7 +33,7 @@ nav.header__secondary > * { @mixin legacy-flex-gap block__header--js-tabbed, var(--padding-normal); @mixin legacy-flex-gap field, var(--padding-normal); @mixin legacy-flex-gap horizontal-list, var(--padding-normal); -@mixin legacy-flex-gap communication-edit__actions, var(--padding-normal); +@mixin legacy-flex-gap communication__edit-actions, var(--padding-normal); @mixin legacy-flex-gap header__link--user, var(--padding-normal); @mixin legacy-flex-gap tag-list, var(--padding-small); @mixin legacy-flex-gap tagsinput, var(--padding-small); diff --git a/assets/css/options/extra-interaction-info.css b/assets/css/options/extra-interaction-info.css new file mode 100644 index 000000000..a4238349c --- /dev/null +++ b/assets/css/options/extra-interaction-info.css @@ -0,0 +1,7 @@ +.interaction--optional { + display: inherit !important; +} + +.media-box__header--link-row { + grid-template-columns: 0.5fr 1.5fr 1fr 1.5fr 1fr 1fr 1fr; +} diff --git a/assets/css/themes/dark-blue.css b/assets/css/themes/dark-blue.css index 5a801a6e2..f96527689 100644 --- a/assets/css/themes/dark-blue.css +++ b/assets/css/themes/dark-blue.css @@ -78,6 +78,9 @@ $spoiler-color: #0f0f0f; --spoiler-color: $spoiler-color; --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); + /* The invisible color */ + --invisible-color: #00000000; + /* Type (information category) colors */ @mixin type-color success, $success-color; @mixin type-color warning, $warning-color; diff --git a/assets/css/themes/dark-purple.css b/assets/css/themes/dark-purple.css index 99cd1766a..fca9dde73 100644 --- a/assets/css/themes/dark-purple.css +++ b/assets/css/themes/dark-purple.css @@ -78,6 +78,9 @@ $spoiler-color: #0f0f0f; --spoiler-color: $spoiler-color; --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); + /* The invisible color */ + --invisible-color: #00000000; + /* Type (information category) colors */ @mixin type-color success, $success-color; @mixin type-color warning, $warning-color; diff --git a/assets/css/themes/light-blue.css b/assets/css/themes/light-blue.css index ff18c34e6..aaa031d5d 100644 --- a/assets/css/themes/light-blue.css +++ b/assets/css/themes/light-blue.css @@ -78,6 +78,9 @@ $spoiler-color: #0f0f0f; --spoiler-color: $spoiler-color; --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); + /* The invisible color */ + --invisible-color: #00000000; + /* Type (information category) colors */ @mixin type-color success, $success-color; @mixin type-color warning, $warning-color; diff --git a/assets/css/views/communication.css b/assets/css/views/communication.css index 241939f23..20669a6a0 100644 --- a/assets/css/views/communication.css +++ b/assets/css/views/communication.css @@ -1,32 +1,42 @@ .communication__toolbar { + display: flex; + flex-wrap: wrap; font-size: var(--font-size); - background: var(--secondary-color); - padding: 0 var(--padding-small); + background: var(--primary-dark-color); + padding: var(--padding-small); line-height: var(--block-header-height); - border: 1px solid var(--secondary-border-color); - border-bottom: 0; - box-sizing: border-box; + border-bottom: 1px solid var(--primary-border-color); } .communication__toolbar__button { - color: var(--text-color); + color: var(--text-color) !important; background: 0; border: 0; - padding: var(--padding-small); + border-radius: var(--border-radius-inner); + padding: 0 var(--padding-small); } .communication__toolbar__button:hover { background: var(--secondary-muted-color); } -.communication-edit__actions { +.communication__edit-tab { + border-radius: var(--border-radius-inner); + overflow: hidden; +} + +.communication__edit-tab textarea { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.communication__edit-actions { display: flex; flex-direction: row; line-height: var(--block-header-height); border: 0; - margin: var(--padding-normal); - margin-top: 0; - gap: var(--padding-normal); + gap: var(--padding-small); + margin-top: var(--padding-small); } .communication__anonymous { diff --git a/assets/css/views/interaction.css b/assets/css/views/interaction.css index e3eeae100..96dbec7e5 100644 --- a/assets/css/views/interaction.css +++ b/assets/css/views/interaction.css @@ -6,6 +6,10 @@ width: 1rem; } + .interaction--$(type).interaction--nohover:hover { + color: var(--$(type)-color) !important; + } + .interaction--$(type).active, .interaction--$(type):hover { color: var(--text-color) !important; @@ -13,6 +17,14 @@ } } +.interaction--optional { + display: none; +} + +.interaction--nohover:hover { + background: 0 !important; +} + @mixin interaction-type fave; @mixin interaction-type upvote; @mixin interaction-type downvote; diff --git a/lib/philomena_web/templates/admin/report/show.html.slime b/lib/philomena_web/templates/admin/report/show.html.slime index 9c227fae7..fd7768fbe 100644 --- a/lib/philomena_web/templates/admin/report/show.html.slime +++ b/lib/philomena_web/templates/admin/report/show.html.slime @@ -4,7 +4,7 @@ p article.block.communication .block__content.flex.flex--no-wrap - = render PhilomenaWeb.CommunicationView, "_body.html", object: @report, body: @body, conn: @conn + = render PhilomenaWeb.CommunicationView, "_body.html", object: @report, body: @body, conn: @conn, name: "report" .block__content.communication__options .flex.flex--wrap.flex--spaced-out diff --git a/lib/philomena_web/templates/communication/_body.html.slime b/lib/philomena_web/templates/communication/_body.html.slime index 90b3cd947..2b35fdf6e 100644 --- a/lib/philomena_web/templates/communication/_body.html.slime +++ b/lib/philomena_web/templates/communication/_body.html.slime @@ -1,7 +1,7 @@ elixir: anon = is_nil(assigns[:noanon]) or @noanon == false options = if is_nil(assigns[:options]), do: "", else: @options - deleted = Map.has_key?(@object, :hidden_from_users) and @object.hidden_from_users == true + deleted = if Map.has_key?(@object, :hidden_from_users), do: @object.hidden_from_users == true, else: false hidden_if_deleted = if deleted, do: "hidden", else: "" deleted_class = if deleted, do: "communication--deleted", else: "" @@ -13,7 +13,6 @@ elixir: = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @object, conn: @conn - true -> = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: @object, conn: @conn, class: "avatar--small" - - username = if anon do = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @object, awards: true, conn: @conn - else diff --git a/lib/philomena_web/templates/conversation/message/_form.html.slime b/lib/philomena_web/templates/conversation/message/_form.html.slime index cbaec375f..f03d7ad3f 100644 --- a/lib/philomena_web/templates/conversation/message/_form.html.slime +++ b/lib/philomena_web/templates/conversation/message/_form.html.slime @@ -1,7 +1,7 @@ = form_for @changeset, ~p"/conversations/#{@conversation}/messages", fn f -> .block - .communication-edit__wrap + .communication__edit-wrap = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, action_icon: "pencil-alt", action_text: "Reply" - .block__content.communication-edit__actions + .block__content.communication__edit-actions = submit "Send", class: "button", autocomplete: "off", data: [disable_with: raw("Sending…")] diff --git a/lib/philomena_web/templates/conversation/new.html.slime b/lib/philomena_web/templates/conversation/new.html.slime index ef0f3758a..6688ad252 100644 --- a/lib/philomena_web/templates/conversation/new.html.slime +++ b/lib/philomena_web/templates/conversation/new.html.slime @@ -38,5 +38,5 @@ h1 New Conversation div = render PhilomenaWeb.MarkdownView, "_input.html", changeset: @changeset, conn: @conn, f: fm, action_icon: "pencil-alt", action_text: "Compose" - .block__content.communication-edit__actions + .block__content.communication__edit-actions = submit "Send", class: "button", autocomplete: "off", data: [disable_with: "Sending..."] diff --git a/lib/philomena_web/templates/image/_image_box.html.slime b/lib/philomena_web/templates/image/_image_box.html.slime index a6a44ce90..ba2516f06 100644 --- a/lib/philomena_web/templates/image/_image_box.html.slime +++ b/lib/philomena_web/templates/image/_image_box.html.slime @@ -23,16 +23,17 @@ elixir: .media-box__header--link-row a.interaction--fave href="#" rel="nofollow" data-image-id=@image.id i.fa.fa-star title="Fave!" + span.interaction--fave.interaction--nohover.interaction--optional.hidden--phone = shorten_number(@image.faves_count) a.interaction--upvote href="#" rel="nofollow" data-image-id=@image.id i.fa.fa-arrow-up title="Yay!" - span.score title="Score" data-image-id=@image.id = @image.score + span.score title="Score" data-image-id=@image.id = shorten_number(@image.score) a.interaction--downvote href="#" rel="nofollow" data-image-id=@image.id i.fa.fa-arrow-down title="Neigh!" + a.interaction--hide.interaction--optional.hidden--phone href="#" rel="nofollow" data-image-id=@image.id + i.fa.fa-eye-slash title="Hide" a.interaction--comment href="/#{@image.id}#comments" title="Comments" i.fa.fa-comments> = if @image.comments_count > 0 do - span.comments-count data-image-id=@image.id = @image.comments_count - /a.interaction--hide href="#" rel="nofollow" data-image-id=@image.id - i.fa.fa-eye-slash title="Hide" + span.comments-count data-image-id=@image.id = shorten_number(@image.comments_count) .media-box__content class=size_class = render PhilomenaWeb.ImageView, "_image_container.html", link: link, image: @image, size: @size, conn: @conn diff --git a/lib/philomena_web/templates/image/comment/_form.html.slime b/lib/philomena_web/templates/image/comment/_form.html.slime index 84bf2bd6d..0d1c4c378 100644 --- a/lib/philomena_web/templates/image/comment/_form.html.slime +++ b/lib/philomena_web/templates/image/comment/_form.html.slime @@ -9,6 +9,6 @@ div = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, placeholder: "Please read the site rules before posting and use ||spoilers|| for above-rating stuff." - .block.communication-edit__actions + .block.communication__edit-actions => submit "Post", class: "button button--important button--success", data: [disable_with: raw("Posting…")] = render PhilomenaWeb.MarkdownView, "_anon_checkbox.html", conn: @conn, f: f diff --git a/lib/philomena_web/templates/image/comment/edit.html.slime b/lib/philomena_web/templates/image/comment/edit.html.slime index 1b17c96a4..236ba1226 100644 --- a/lib/philomena_web/templates/image/comment/edit.html.slime +++ b/lib/philomena_web/templates/image/comment/edit.html.slime @@ -4,12 +4,12 @@ p Oops, something went wrong! Please check the errors below. .block - .communication-edit__wrap + .communication__edit-wrap = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, placeholder: "Please read the site rules before posting and use ||spoilers|| for above-rating stuff." .block__content.field = text_input f, :edit_reason, class: "input input--wide", placeholder: "Reason for edit" = error_tag f, :edit_reason - .block__content.communication-edit__actions + .block__content.communication__edit-actions => submit "Edit", class: "button", data: [disable_with: raw("Posting…")] diff --git a/lib/philomena_web/templates/image/description/_form.html.slime b/lib/philomena_web/templates/image/description/_form.html.slime index e47b5d3ff..1f4fd36b7 100644 --- a/lib/philomena_web/templates/image/description/_form.html.slime +++ b/lib/philomena_web/templates/image/description/_form.html.slime @@ -3,7 +3,6 @@ .alert.alert-danger p Oops, something went wrong! Please check the errors below. - = render PhilomenaWeb.MarkdownView, "_help.html", conn: @conn = render PhilomenaWeb.MarkdownView, "_toolbar.html", conn: @conn .field diff --git a/lib/philomena_web/templates/image/new.html.slime b/lib/philomena_web/templates/image/new.html.slime index 68fe74716..42d9f940f 100644 --- a/lib/philomena_web/templates/image/new.html.slime +++ b/lib/philomena_web/templates/image/new.html.slime @@ -80,7 +80,7 @@ .field .block - .communication-edit__wrap + .communication__edit-wrap = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, action_icon: "pencil-alt", action_text: "Description", placeholder: "Describe this image in plain words - this should generally be info about the image that doesn't belong in the tags or source.", name: :description, class: "js-image-descr-input", required: false = render PhilomenaWeb.MarkdownView, "_anon_checkbox.html", conn: @conn, f: f, label: "Post anonymously" diff --git a/lib/philomena_web/templates/markdown/_help.html.slime b/lib/philomena_web/templates/markdown/_help.html.slime deleted file mode 100644 index e2838ea3e..000000000 --- a/lib/philomena_web/templates/markdown/_help.html.slime +++ /dev/null @@ -1,17 +0,0 @@ -.editor-syntax-reference - strong<> Syntax quick reference: - - span - strong> **bold** - em> *italic* - span.spoiler> ||hide text|| - code> `code` - ins> __underline__ - del> ~~strike~~ - sup> ^sup^ - sub %sub% - - p - a href="/pages/markdown" - i.fa.fa-question-circle> - strong Detailed syntax guide diff --git a/lib/philomena_web/templates/markdown/_input.html.slime b/lib/philomena_web/templates/markdown/_input.html.slime index 8e30b6275..50724fc5a 100644 --- a/lib/philomena_web/templates/markdown/_input.html.slime +++ b/lib/philomena_web/templates/markdown/_input.html.slime @@ -6,21 +6,20 @@ - is_required = assigns[:required] - input_classes = assigns[:class] .block__header.block__header--js-tabbed - a.selected href="#" data-click-tab="write" + a.button.selected href="#" data-click-tab="write" i.fa> class="fa-#{action_icon}" = action_text - a href="#" data-click-tab="preview" - i.fa.fa-cog.icon--padded.small.fa-spin.js-preview-loading.hidden> title=raw('Loading preview…') - i.fa.fa-eye.icon--padded.small.js-preview-idle> + a.button href="#" data-click-tab="preview" + i.fa.fa-cog.fa-spin.js-preview-loading.hidden> title=raw('Loading preview…') + i.fa.fa-eye.js-preview-idle> | Preview -.block__tab.communication-edit__tab.selected.js-preview-input-wrapper data-tab="write" - = render PhilomenaWeb.MarkdownView, "_help.html", conn: @conn +.block__tab.communication__edit-tab.selected.js-preview-input-wrapper data-tab="write" = render PhilomenaWeb.MarkdownView, "_toolbar.html", conn: @conn .field = textarea form, field_name, class: add_classes("input input--wide input--text input--resize-vertical js-toolbar-input js-preview-input", input_classes), placeholder: field_placeholder, required: required?(is_required) = error_tag form, field_name -.block__tab.communication-edit__tab.hidden.js-preview-content data-tab="preview" +.block__tab.block__content.communication__edit-tab.hidden.js-preview-content data-tab="preview" diff --git a/lib/philomena_web/templates/markdown/_toolbar.html.slime b/lib/philomena_web/templates/markdown/_toolbar.html.slime index df4e7cf71..6a40884bb 100644 --- a/lib/philomena_web/templates/markdown/_toolbar.html.slime +++ b/lib/philomena_web/templates/markdown/_toolbar.html.slime @@ -1,4 +1,4 @@ -.communication__toolbar.flex.flex--wrap +.communication__toolbar button.communication__toolbar__button tabindex="-1" type="button" title="bold (ctrl+b)" data-syntax-id="bold" strong | B @@ -23,14 +23,15 @@ button.communication__toolbar__button tabindex="-1" type="button" title="subscript" data-syntax-id="subscript" sub | sub - .separator--vertical.separator--secondary button.communication__toolbar__button tabindex="-1" type="button" title="insert blockquote" data-syntax-id="quote" i.fa.fa-quote-right button.communication__toolbar__button tabindex="-1" type="button" title="insert hyperlink (ctrl+l)" data-syntax-id="link" i.fa.fa-link button.communication__toolbar__button tabindex="-1" type="button" title="insert image (ctrl+k)" data-syntax-id="image" i.fa.fa-image - .separator--vertical.separator--secondary button.communication__toolbar__button tabindex="-1" type="button" title="Text you want the parser to ignore" data-syntax-id="escape" span | escape + .flex__spacer + a.communication__toolbar__button href="/pages/markdown" + i.fa.fa-question-circle diff --git a/lib/philomena_web/templates/message/_message.html.slime b/lib/philomena_web/templates/message/_message.html.slime index 09864ccfb..1b67d866e 100644 --- a/lib/philomena_web/templates/message/_message.html.slime +++ b/lib/philomena_web/templates/message/_message.html.slime @@ -14,10 +14,4 @@ article.block.communication ' Approve .block__content.flex.flex--no-wrap - = render PhilomenaWeb.CommunicationView, "_body.html", object: %{user: @message.from}, noanon: true, body: @body, conn: @conn, name: "message" - - .block__content.communication__options - .flex.flex--wrap.flex--spaced-out - div - ' Posted - = pretty_time(@message.created_at) + = render PhilomenaWeb.CommunicationView, "_body.html", object: %{user: @message.from, created_at: @message.created_at}, noanon: true, body: @body, conn: @conn, name: "message" diff --git a/lib/philomena_web/templates/profile/description/edit.html.slime b/lib/philomena_web/templates/profile/description/edit.html.slime index 5174098cf..de0375fac 100644 --- a/lib/philomena_web/templates/profile/description/edit.html.slime +++ b/lib/philomena_web/templates/profile/description/edit.html.slime @@ -17,5 +17,5 @@ h1 Updating Profile Description div = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, action_text: "About Me", placeholder: "Description (up to 10000 characters)", name: :description, required: false - .block.communication-edit__actions + .block.communication__edit-actions => submit "Update", class: "button button--important button--success" diff --git a/lib/philomena_web/templates/profile/scratchpad/edit.html.slime b/lib/philomena_web/templates/profile/scratchpad/edit.html.slime index fd1fea171..a87045adf 100644 --- a/lib/philomena_web/templates/profile/scratchpad/edit.html.slime +++ b/lib/philomena_web/templates/profile/scratchpad/edit.html.slime @@ -8,5 +8,5 @@ h1 Updating Moderation Scratchpad div = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, action_text: "Scratchpad", placeholder: "Scratchpad Contents", name: :scratchpad, required: false - .block.communication-edit__actions + .block.communication__edit-actions => submit "Update", class: "button button--important button--success" diff --git a/lib/philomena_web/templates/topic/new.html.slime b/lib/philomena_web/templates/topic/new.html.slime index 05236f7aa..314d1a838 100644 --- a/lib/philomena_web/templates/topic/new.html.slime +++ b/lib/philomena_web/templates/topic/new.html.slime @@ -14,7 +14,7 @@ = inputs_for f, :posts, fn fp -> = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: fp, action_icon: "pencil-alt", action_text: "First Post", placeholder: "Please read the site rules before posting and use ||spoilers|| for NSFW stuff in SFW forums." - .block__content.communication-edit__wrap + .block__content.communication__edit-wrap = render PhilomenaWeb.MarkdownView, "_anon_checkbox.html", conn: @conn, f: f, label: "Post anonymously" = inputs_for f, :poll, fn fp -> @@ -24,5 +24,5 @@ .toggle-box-container = render PhilomenaWeb.Topic.PollView, "_form.html", Map.put(assigns, :f, fp) - .block__content.communication-edit__actions + .block__content.communication__edit-actions = submit "Post", class: "button", data: [disable_with: raw("Posting…")] diff --git a/lib/philomena_web/templates/topic/post/_form.html.slime b/lib/philomena_web/templates/topic/post/_form.html.slime index dcb15f40a..b87e56c6a 100644 --- a/lib/philomena_web/templates/topic/post/_form.html.slime +++ b/lib/philomena_web/templates/topic/post/_form.html.slime @@ -7,7 +7,7 @@ div = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, placeholder: "Please read the site rules before posting and use ||spoilers|| for NSFW stuff in SFW forums." - .block__content.communication-edit__actions + .block__content.communication__edit-actions => submit "Post", class: "button", data: [disable_with: raw("Posting…")] = render PhilomenaWeb.MarkdownView, "_anon_checkbox.html", conn: @conn, f: f diff --git a/lib/philomena_web/templates/topic/post/edit.html.slime b/lib/philomena_web/templates/topic/post/edit.html.slime index b75441216..cf387e7b2 100644 --- a/lib/philomena_web/templates/topic/post/edit.html.slime +++ b/lib/philomena_web/templates/topic/post/edit.html.slime @@ -4,7 +4,7 @@ p Oops, something went wrong! Please check the errors below. .block - .communication-edit__wrap + .communication__edit-wrap .field = render PhilomenaWeb.MarkdownView, "_input.html", conn: @conn, f: f, placeholder: "Please read the site rules before posting and use ||spoilers|| for NSFW stuff in SFW forums." @@ -12,5 +12,5 @@ = text_input f, :edit_reason, class: "input input--wide", placeholder: "Reason for edit" = error_tag f, :edit_reason - .block__content.communication-edit__actions + .block__content.communication__edit-actions => submit "Edit", class: "button", data: [disable_with: raw("Posting…")] diff --git a/lib/philomena_web/views/app_view.ex b/lib/philomena_web/views/app_view.ex index eadfa406b..d6f3a7d88 100644 --- a/lib/philomena_web/views/app_view.ex +++ b/lib/philomena_web/views/app_view.ex @@ -258,4 +258,9 @@ defmodule PhilomenaWeb.AppView do def get_flash(_, _key), do: %{} def site_name, do: gettext("Philomena Site") + + def shorten_number(n) when n >= 1_000_000_000, do: "#{Float.floor(n / 1_000_000_000, 1)}B" + def shorten_number(n) when n >= 1_000_000, do: "#{Float.floor(n / 1_000_000, 1)}M" + def shorten_number(n) when n >= 1_000, do: "#{Float.floor(n / 1_000, 1)}K" + def shorten_number(n), do: n end diff --git a/priv/repo/seeds/pages/rules.md b/priv/repo/seeds/pages/rules.md index e69de29bb..4f2e970e9 100644 --- a/priv/repo/seeds/pages/rules.md +++ b/priv/repo/seeds/pages/rules.md @@ -0,0 +1 @@ +PLACEHOLDER From d39556d413a16e9e54b267ff609753b259ed0965 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 12 Jun 2024 21:01:29 +0200 Subject: [PATCH 41/65] npm update --- assets/package-lock.json | 1007 +++++++++++++++++++------------------- assets/package.json | 22 +- 2 files changed, 515 insertions(+), 514 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index d2c17782c..07f86d295 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -5,15 +5,15 @@ "packages": { "": { "dependencies": { - "@csstools/postcss-relative-color-syntax": "^2.0.14", + "@csstools/postcss-relative-color-syntax": "^2.0.16", "@fortawesome/fontawesome-free": "^6.5.2", "@types/postcss-mixins": "^9.0.5", - "@types/web": "^0.0.143", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", + "@types/web": "^0.0.148", + "@typescript-eslint/eslint-plugin": "^7.13.0", + "@typescript-eslint/parser": "^7.13.0", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", - "eslint": "^8.34.0", + "eslint": "^8.57.0", "jest-environment-jsdom": "^29.7.0", "normalize.css": "^8.0.1", "postcss-mixins": "^10.0.1", @@ -23,24 +23,24 @@ }, "devDependencies": { "@testing-library/dom": "^10.1.0", - "@testing-library/jest-dom": "^6.4.2", + "@testing-library/jest-dom": "^6.4.6", "@types/chai-dom": "^1.11.3", - "@vitest/coverage-v8": "^1.5.3", + "@vitest/coverage-v8": "^1.6.0", "chai": "^5", "eslint-plugin-vitest": "^0.5.4", - "jsdom": "^24.0.0", - "prettier": "^3.2.5", - "stylelint": "^16.5.0", + "jsdom": "^24.1.0", + "prettier": "^3.3.2", + "stylelint": "^16.6.1", "stylelint-config-standard": "^36.0.0", "stylelint-prettier": "^5.0.0", - "vitest": "^1.5.3", + "vitest": "^1.6.0", "vitest-fetch-mock": "^0.2.2" } }, "node_modules/@adobe/css-tools": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", - "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", "dev": true }, "node_modules/@ampproject/remapping": { @@ -57,11 +57,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" }, "engines": { @@ -69,28 +69,28 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", + "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -164,9 +164,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", - "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -176,9 +176,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -188,13 +188,13 @@ } }, "node_modules/@babel/types": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", - "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", + "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.24.1", - "@babel/helper-validator-identifier": "^7.24.5", + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -226,9 +226,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.2.0.tgz", - "integrity": "sha512-iQqIW5vDPqQdLx07/atCuNKDprhIWjB0b8XRhUyXZWBZYUG+9mNyFwyu30rypX84WLevVo25NYW2ipxR8WyseQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.2.2.tgz", + "integrity": "sha512-0owrl7AruDRKAxoSIW8XzJdz7GnuW3AOj4rYLfmXsoKIX2ZZzttzGXoiC8n8V08X7wIBlEWWVB4C8fAN18+I6Q==", "funding": [ { "type": "github", @@ -243,14 +243,14 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.1", - "@csstools/css-tokenizer": "^2.2.4" + "@csstools/css-parser-algorithms": "^2.6.3", + "@csstools/css-tokenizer": "^2.3.1" } }, "node_modules/@csstools/css-color-parser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-2.0.0.tgz", - "integrity": "sha512-0/v6OPpcg+b8TJT2N1Rcp0oH5xEvVOU5K2qDkaR3IMHNXuJ7XfVCQLINt3Cuj8mr54DbilEoZ9uvAmHBoZ//Fw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-2.0.2.tgz", + "integrity": "sha512-Agx2YmxTcZ7TfB7KNZQ+iekaxbWSdblvtA35aTwE3KfuYyjOlCg3P4KGGdQF/cjm1pHWVSBo5duF/BRfZ8s07A==", "funding": [ { "type": "github", @@ -263,14 +263,14 @@ ], "dependencies": { "@csstools/color-helpers": "^4.2.0", - "@csstools/css-calc": "^1.2.0" + "@csstools/css-calc": "^1.2.2" }, "engines": { "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.1", - "@csstools/css-tokenizer": "^2.2.4" + "@csstools/css-parser-algorithms": "^2.6.3", + "@csstools/css-tokenizer": "^2.3.1" } }, "node_modules/@csstools/css-parser-algorithms": { @@ -360,9 +360,9 @@ } }, "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.14.tgz", - "integrity": "sha512-NlxgLjAjVCTUVGiWk8WNj3dKvux9eC6O5aLM3BmdA8UXEwBHYI9r4IqlanxG9PlcXnzhTUX6eZsqgmxwt4FPow==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.16.tgz", + "integrity": "sha512-TSM8fVqJkT8JZDranZPnkpxjU/Q1sNR192lXMND+EcKOUjYa6uYpGSfHgjnWjCRiBSciettS+sL7y9wmnas7qQ==", "funding": [ { "type": "github", @@ -374,9 +374,9 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^2.0.0", - "@csstools/css-parser-algorithms": "^2.6.1", - "@csstools/css-tokenizer": "^2.2.4", + "@csstools/css-color-parser": "^2.0.2", + "@csstools/css-parser-algorithms": "^2.6.3", + "@csstools/css-tokenizer": "^2.3.1", "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, @@ -800,9 +800,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", + "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -870,6 +870,7 @@ "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", "debug": "^4.3.1", @@ -914,7 +915,8 @@ "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==" + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead" }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", @@ -1063,9 +1065,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", "cpu": [ "arm" ], @@ -1075,9 +1077,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", "cpu": [ "arm64" ], @@ -1087,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", - "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", "cpu": [ "arm64" ], @@ -1099,9 +1101,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", "cpu": [ "x64" ], @@ -1111,9 +1113,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", "cpu": [ "arm" ], @@ -1123,9 +1125,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", "cpu": [ "arm" ], @@ -1135,9 +1137,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", "cpu": [ "arm64" ], @@ -1147,9 +1149,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", "cpu": [ "arm64" ], @@ -1159,9 +1161,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", "cpu": [ "ppc64" ], @@ -1171,9 +1173,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", "cpu": [ "riscv64" ], @@ -1183,9 +1185,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", "cpu": [ "s390x" ], @@ -1195,9 +1197,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", "cpu": [ "x64" ], @@ -1207,9 +1209,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", "cpu": [ "x64" ], @@ -1219,9 +1221,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", "cpu": [ "arm64" ], @@ -1231,9 +1233,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", "cpu": [ "ia32" ], @@ -1243,9 +1245,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", "cpu": [ "x64" ], @@ -1295,18 +1297,18 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz", - "integrity": "sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.6.tgz", + "integrity": "sha512-8qpnGVincVDLEcQXWaHOf6zmlbwTKc6Us6PPu4CRnPXCzo2OGBS5cwgMMOWdxDpEz1mkbvXHpEy99M5Yvt682w==", "dev": true, "dependencies": { - "@adobe/css-tools": "^4.3.2", + "@adobe/css-tools": "^4.4.0", "@babel/runtime": "^7.9.2", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "redent": "^3.0.0" }, "engines": { @@ -1373,9 +1375,9 @@ "dev": true }, "node_modules/@types/chai": { - "version": "4.3.14", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.14.tgz", - "integrity": "sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w==", + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", + "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", "dev": true }, "node_modules/@types/chai-dom": { @@ -1423,15 +1425,10 @@ "parse5": "^7.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", + "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", "dependencies": { "undici-types": "~5.26.4" } @@ -1444,11 +1441,6 @@ "postcss": "^8.2.14" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -1460,9 +1452,9 @@ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" }, "node_modules/@types/web": { - "version": "0.0.143", - "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.143.tgz", - "integrity": "sha512-TazK16/OqeeqfQRB/Tv/NwzJagHbLi/w5g26FLbiFte/8LpPq6BuTyXHO/cpgwJpE6KGgFSNYb6Ap05Tz9XvCA==" + "version": "0.0.148", + "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.148.tgz", + "integrity": "sha512-HX2eARbn26tZuCOxZ25Ew6UUNhw8fgdGrOGcxX0/J6yTtlJm+nHlL9/h+2zgSzse13vlVe+c+W3LWqhnlAd5rg==" }, "node_modules/@types/yargs": { "version": "17.0.32", @@ -1478,20 +1470,18 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.8.0.tgz", - "integrity": "sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz", + "integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/type-utils": "7.8.0", - "@typescript-eslint/utils": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", - "debug": "^4.3.4", + "@typescript-eslint/scope-manager": "7.13.0", + "@typescript-eslint/type-utils": "7.13.0", + "@typescript-eslint/utils": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { @@ -1512,14 +1502,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.8.0.tgz", - "integrity": "sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==", - "dependencies": { - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz", + "integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==", + "dependencies": { + "@typescript-eslint/scope-manager": "7.13.0", + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/typescript-estree": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0", "debug": "^4.3.4" }, "engines": { @@ -1539,12 +1529,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.8.0.tgz", - "integrity": "sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz", + "integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==", "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0" + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1555,12 +1545,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.8.0.tgz", - "integrity": "sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz", + "integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==", "dependencies": { - "@typescript-eslint/typescript-estree": "7.8.0", - "@typescript-eslint/utils": "7.8.0", + "@typescript-eslint/typescript-estree": "7.13.0", + "@typescript-eslint/utils": "7.13.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1581,9 +1571,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.8.0.tgz", - "integrity": "sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz", + "integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -1593,12 +1583,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.8.0.tgz", - "integrity": "sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz", + "integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==", "dependencies": { - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/visitor-keys": "7.8.0", + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1620,17 +1610,14 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.8.0.tgz", - "integrity": "sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz", + "integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.8.0", - "@typescript-eslint/types": "7.8.0", - "@typescript-eslint/typescript-estree": "7.8.0", - "semver": "^7.6.0" + "@typescript-eslint/scope-manager": "7.13.0", + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/typescript-estree": "7.13.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1644,11 +1631,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.8.0.tgz", - "integrity": "sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz", + "integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==", "dependencies": { - "@typescript-eslint/types": "7.8.0", + "@typescript-eslint/types": "7.13.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1665,9 +1652,9 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "node_modules/@vitest/coverage-v8": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.3.tgz", - "integrity": "sha512-DPyGSu/fPHOJuPxzFSQoT4N/Fu/2aJfZRtEpEp8GI7NHsXBGE94CQ+pbEGBUMFjatsHPDJw/+TAF9r4ens2CNw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", + "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -1688,17 +1675,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.5.3" + "vitest": "1.6.0" } }, "node_modules/@vitest/expect": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.3.tgz", - "integrity": "sha512-y+waPz31pOFr3rD7vWTbwiLe5+MgsMm40jTZbQE8p8/qXyBX3CQsIXRx9XK12IbY7q/t5a5aM/ckt33b4PxK2g==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dev": true, "dependencies": { - "@vitest/spy": "1.5.3", - "@vitest/utils": "1.5.3", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "chai": "^4.3.10" }, "funding": { @@ -1745,9 +1732,9 @@ } }, "node_modules/@vitest/expect/node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, "dependencies": { "type-detect": "^4.0.0" @@ -1756,6 +1743,15 @@ "node": ">=6" } }, + "node_modules/@vitest/expect/node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/@vitest/expect/node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -1766,12 +1762,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.3.tgz", - "integrity": "sha512-7PlfuReN8692IKQIdCxwir1AOaP5THfNkp0Uc4BKr2na+9lALNit7ub9l3/R7MP8aV61+mHKRGiqEKRIwu6iiQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dev": true, "dependencies": { - "@vitest/utils": "1.5.3", + "@vitest/utils": "1.6.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -1807,9 +1803,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.3.tgz", - "integrity": "sha512-K3mvIsjyKYBhNIDujMD2gfQEzddLe51nNOAf45yKRt/QFJcUIeTQd2trRvv6M6oCBHNVnZwFWbQ4yj96ibiDsA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -1853,9 +1849,9 @@ "dev": true }, "node_modules/@vitest/spy": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.3.tgz", - "integrity": "sha512-Llj7Jgs6lbnL55WoshJUUacdJfjU2honvGcAJBxhra5TPEzTJH8ZuhI3p/JwqqfnTr4PmP7nDmOXP53MS7GJlg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -1865,9 +1861,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.3.tgz", - "integrity": "sha512-rE9DTN1BRhzkzqNQO+kw8ZgfeEBCLXiHJwetk668shmNBpSagQxneT5eSqEBLP+cqSiAeecvQmbpFfdMyLcIQA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -1891,6 +1887,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@vitest/utils/node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/@vitest/utils/node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -2097,20 +2102,20 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "funding": [ { "type": "opencollective", @@ -2126,10 +2131,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -2164,9 +2169,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001614", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz", - "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==", + "version": "1.0.30001632", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001632.tgz", + "integrity": "sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==", "funding": [ { "type": "opencollective", @@ -2183,13 +2188,13 @@ ] }, "node_modules/chai": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.0.tgz", - "integrity": "sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", "dev": true, "dependencies": { "assertion-error": "^2.0.1", - "check-error": "^2.0.0", + "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" @@ -2198,15 +2203,6 @@ "node": ">=12" } }, - "node_modules/chai/node_modules/loupe": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.0.tgz", - "integrity": "sha512-qKl+FrLXUhFuHUoDJG7f8P8gEMHq9NFS0c6ghXG1J0rldmZFQZoNVv/vyirE9qwCIhWZDsvEFd1sbFu3GvRQFg==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2223,9 +2219,9 @@ } }, "node_modules/check-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.0.0.tgz", - "integrity": "sha512-tjLAOBHKVxtPoHe/SA7kNOMvhCRdCJ3vETdeY0RuAc9popf+hyaSV6ZEg9hr4cpWF7jmo/JSWEnLDrnijS9Tog==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, "engines": { "node": ">= 16" @@ -2411,6 +2407,12 @@ "node": ">=18" } }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "dev": true + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -2425,9 +2427,9 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -2446,9 +2448,9 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, "node_modules/deep-eql": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.1.tgz", - "integrity": "sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "engines": { "node": ">=6" @@ -2526,9 +2528,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.751", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.751.tgz", - "integrity": "sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==" + "version": "1.4.799", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.799.tgz", + "integrity": "sha512-3D3DwWkRTzrdEpntY0hMLYwj7SeBk1138CkPE8sBDSj3WzrzOiG2rHm3luw8jucpf+WiyLBCZyU9lMHyQI9M9Q==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2840,6 +2842,29 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2916,9 +2941,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -3011,10 +3036,23 @@ "node": "*" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3212,6 +3250,15 @@ "node": ">= 14" } }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -3267,6 +3314,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3347,6 +3395,18 @@ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3703,9 +3763,9 @@ } }, "node_modules/jsdom": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", - "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.0.tgz", + "integrity": "sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==", "dev": true, "dependencies": { "cssstyle": "^4.0.1", @@ -3713,21 +3773,21 @@ "decimal.js": "^10.4.3", "form-data": "^4.0.0", "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.4", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.7", + "nwsapi": "^2.2.10", "parse5": "^7.1.2", - "rrweb-cssom": "^0.6.0", + "rrweb-cssom": "^0.7.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.3", + "tough-cookie": "^4.1.4", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0", - "ws": "^8.16.0", + "ws": "^8.17.0", "xml-name-validator": "^5.0.0" }, "engines": { @@ -3781,9 +3841,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.30.0.tgz", - "integrity": "sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==", + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.31.0.tgz", + "integrity": "sha512-sBPIUGTNF0czz0mwGGUoKKJC8Q7On1GPbCSFPfyEsfHb2DyBG0Y4QtV+EVWpINSaiGKZblDNuF5AezxSgOhesQ==", "dev": true }, "node_modules/levn": { @@ -3852,25 +3912,14 @@ "dev": true }, "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", "dev": true, "dependencies": { "get-func-name": "^2.0.1" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -3958,11 +4007,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -3988,6 +4037,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -4012,15 +4073,15 @@ } }, "node_modules/mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", "dev": true, "dependencies": { "acorn": "^8.11.3", "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" } }, "node_modules/ms": { @@ -4119,10 +4180,37 @@ "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/nwsapi": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", - "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==" + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", + "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==" }, "node_modules/once": { "version": "1.4.0", @@ -4132,6 +4220,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4264,9 +4367,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -4280,13 +4383,13 @@ } }, "node_modules/pkg-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", - "integrity": "sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", + "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==", "dev": true, "dependencies": { "confbox": "^0.1.7", - "mlly": "^1.6.1", + "mlly": "^1.7.0", "pathe": "^1.1.2" } }, @@ -4395,9 +4498,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -4436,9 +4539,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -4585,6 +4688,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, @@ -4596,9 +4700,9 @@ } }, "node_modules/rollup": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", "dependencies": { "@types/estree": "1.0.5" }, @@ -4610,29 +4714,29 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.17.2", - "@rollup/rollup-android-arm64": "4.17.2", - "@rollup/rollup-darwin-arm64": "4.17.2", - "@rollup/rollup-darwin-x64": "4.17.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", - "@rollup/rollup-linux-arm64-gnu": "4.17.2", - "@rollup/rollup-linux-arm64-musl": "4.17.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", - "@rollup/rollup-linux-s390x-gnu": "4.17.2", - "@rollup/rollup-linux-x64-gnu": "4.17.2", - "@rollup/rollup-linux-x64-musl": "4.17.2", - "@rollup/rollup-win32-arm64-msvc": "4.17.2", - "@rollup/rollup-win32-ia32-msvc": "4.17.2", - "@rollup/rollup-win32-x64-msvc": "4.17.2", + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", "fsevents": "~2.3.2" } }, "node_modules/rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "dev": true }, "node_modules/run-parallel": { @@ -4674,12 +4778,9 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -4822,6 +4923,18 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -4864,16 +4977,26 @@ "dev": true }, "node_modules/stylelint": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.5.0.tgz", - "integrity": "sha512-IlCBtVrG+qTy3v+tZTk50W8BIomjY/RUuzdrDqdnlCYwVuzXtPbiGfxYqtyYAyOMcb+195zRsuHn6tgfPmFfbw==", + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.6.1.tgz", + "integrity": "sha512-yNgz2PqWLkhH2hw6X9AweV9YvoafbAD5ZsFdKN9BvSDVwGvPh+AUIrn7lYwy1S7IHmtFin75LLfX1m0D2tHu8Q==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "dependencies": { - "@csstools/css-parser-algorithms": "^2.6.1", - "@csstools/css-tokenizer": "^2.2.4", - "@csstools/media-query-list-parser": "^2.1.9", - "@csstools/selector-specificity": "^3.0.3", - "@dual-bundle/import-meta-resolve": "^4.0.0", + "@csstools/css-parser-algorithms": "^2.6.3", + "@csstools/css-tokenizer": "^2.3.1", + "@csstools/media-query-list-parser": "^2.1.11", + "@csstools/selector-specificity": "^3.1.1", + "@dual-bundle/import-meta-resolve": "^4.1.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", @@ -4882,7 +5005,7 @@ "debug": "^4.3.4", "fast-glob": "^3.3.2", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "^9.0.0", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", @@ -4890,16 +5013,16 @@ "ignore": "^5.3.1", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.30.0", + "known-css-properties": "^0.31.0", "mathml-tag-names": "^2.1.3", "meow": "^13.2.0", - "micromatch": "^4.0.5", + "micromatch": "^4.0.7", "normalize-path": "^3.0.0", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss": "^8.4.38", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^7.0.0", - "postcss-selector-parser": "^6.0.16", + "postcss-selector-parser": "^6.1.0", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", @@ -4914,10 +5037,6 @@ }, "engines": { "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" } }, "node_modules/stylelint-config-recommended": { @@ -4982,28 +5101,28 @@ "dev": true }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", + "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", "dev": true, "dependencies": { - "flat-cache": "^4.0.0" + "flat-cache": "^5.0.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", "dev": true, "dependencies": { - "flatted": "^3.2.9", + "flatted": "^3.3.1", "keyv": "^4.5.4" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/stylelint/node_modules/resolve-from": { @@ -5097,9 +5216,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", + "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.3", @@ -5302,9 +5421,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "funding": [ { "type": "opencollective", @@ -5320,8 +5439,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -5354,9 +5473,9 @@ "dev": true }, "node_modules/vite": { - "version": "5.2.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz", - "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==", + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", + "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", @@ -5408,9 +5527,9 @@ } }, "node_modules/vite-node": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.3.tgz", - "integrity": "sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -5430,16 +5549,16 @@ } }, "node_modules/vitest": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.3.tgz", - "integrity": "sha512-2oM7nLXylw3mQlW6GXnRriw+7YvZFk/YNV8AxIC3Z3MfFbuziLGWP9GPxxu/7nRlXhqyxBikpamr+lEEj1sUEw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", "dev": true, "dependencies": { - "@vitest/expect": "1.5.3", - "@vitest/runner": "1.5.3", - "@vitest/snapshot": "1.5.3", - "@vitest/spy": "1.5.3", - "@vitest/utils": "1.5.3", + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -5453,7 +5572,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.5.3", + "vite-node": "1.6.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -5468,8 +5587,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.5.3", - "@vitest/ui": "1.5.3", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", "happy-dom": "*", "jsdom": "*" }, @@ -5549,9 +5668,9 @@ } }, "node_modules/vitest/node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, "dependencies": { "type-detect": "^4.0.0" @@ -5560,114 +5679,13 @@ "node": ">=6" } }, - "node_modules/vitest/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/vitest/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/vitest/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/vitest/node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "get-func-name": "^2.0.1" } }, "node_modules/vitest/node_modules/pathval": { @@ -5679,18 +5697,6 @@ "node": "*" } }, - "node_modules/vitest/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -5835,11 +5841,6 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/assets/package.json b/assets/package.json index 0cd8539a1..de267bd4e 100644 --- a/assets/package.json +++ b/assets/package.json @@ -10,15 +10,15 @@ "preview": "vite preview" }, "dependencies": { - "@csstools/postcss-relative-color-syntax": "^2.0.14", + "@csstools/postcss-relative-color-syntax": "^2.0.16", "@fortawesome/fontawesome-free": "^6.5.2", "@types/postcss-mixins": "^9.0.5", - "@types/web": "^0.0.143", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", + "@types/web": "^0.0.148", + "@typescript-eslint/eslint-plugin": "^7.13.0", + "@typescript-eslint/parser": "^7.13.0", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", - "eslint": "^8.34.0", + "eslint": "^8.57.0", "jest-environment-jsdom": "^29.7.0", "normalize.css": "^8.0.1", "postcss-mixins": "^10.0.1", @@ -28,17 +28,17 @@ }, "devDependencies": { "@testing-library/dom": "^10.1.0", - "@testing-library/jest-dom": "^6.4.2", + "@testing-library/jest-dom": "^6.4.6", "@types/chai-dom": "^1.11.3", - "@vitest/coverage-v8": "^1.5.3", + "@vitest/coverage-v8": "^1.6.0", "chai": "^5", "eslint-plugin-vitest": "^0.5.4", - "jsdom": "^24.0.0", - "prettier": "^3.2.5", - "stylelint": "^16.5.0", + "jsdom": "^24.1.0", + "prettier": "^3.3.2", + "stylelint": "^16.6.1", "stylelint-config-standard": "^36.0.0", "stylelint-prettier": "^5.0.0", - "vitest": "^1.5.3", + "vitest": "^1.6.0", "vitest-fetch-mock": "^0.2.2" } } From 66f91be92ac212e93e6604ebb167cfbe1c814c31 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 12 Jun 2024 22:56:58 +0200 Subject: [PATCH 42/65] eslint 9 --- assets/.browserslistrc | 2 - assets/.eslintignore | 2 - assets/.eslintrc.yml | 289 ---------------- assets/eslint.config.js | 293 ++++++++++++++++ assets/js/booru.js | 2 +- assets/js/fp.ts | 2 +- assets/js/imagesclientside.ts | 3 +- assets/js/search.ts | 10 +- assets/js/settings.ts | 12 +- assets/js/shortcuts.ts | 5 +- assets/js/tagsmisc.ts | 2 +- assets/js/utils/store.ts | 8 +- assets/package-lock.json | 635 +++++++++++++++++++--------------- assets/package.json | 7 +- assets/test/vitest-setup.ts | 1 - 15 files changed, 668 insertions(+), 605 deletions(-) delete mode 100644 assets/.eslintignore delete mode 100644 assets/.eslintrc.yml create mode 100644 assets/eslint.config.js diff --git a/assets/.browserslistrc b/assets/.browserslistrc index 41e7f1c16..ee6762d03 100644 --- a/assets/.browserslistrc +++ b/assets/.browserslistrc @@ -2,8 +2,6 @@ last 2 Android versions last 2 Chrome versions last 2 ChromeAndroid versions last 2 Edge versions -last 1 Explorer version -last 1 ExplorerMobile versions last 2 Firefox versions last 2 FirefoxAndroid versions last 2 iOS versions diff --git a/assets/.eslintignore b/assets/.eslintignore deleted file mode 100644 index 53c47bd81..000000000 --- a/assets/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -js/vendor/* -vite.config.ts diff --git a/assets/.eslintrc.yml b/assets/.eslintrc.yml deleted file mode 100644 index f59832452..000000000 --- a/assets/.eslintrc.yml +++ /dev/null @@ -1,289 +0,0 @@ -env: - browser: true - es6: true - -parser: '@typescript-eslint/parser' - -parserOptions: - ecmaVersion: 6 - sourceType: module - -plugins: - - '@typescript-eslint' - - vitest - -globals: - ga: false - md5: false - Sortable: false - ActionCable: false - -extends: - - 'plugin:@typescript-eslint/recommended' - -rules: - accessor-pairs: 2 - array-bracket-spacing: 0 - array-callback-return: 2 - arrow-body-style: 0 - arrow-parens: [2, 'as-needed'] - arrow-spacing: 2 - block-scoped-var: 2 - block-spacing: 2 - brace-style: [2, 'stroustrup', {allowSingleLine: true}] - callback-return: 0 - camelcase: [2, {allow: ['camo_url', 'spoiler_image_uri','image_ids']}] - class-methods-use-this: 0 - comma-dangle: [2, 'only-multiline'] - comma-spacing: 2 - comma-style: 2 - complexity: 0 - computed-property-spacing: [2, 'never'] - consistent-return: 0 - consistent-this: [2, 'that'] - constructor-super: 2 - curly: [2, 'multi-line', 'consistent'] - default-case: 2 - dot-location: [2, 'property'] - dot-notation: [2, {allowKeywords: true}] - eol-last: 2 - eqeqeq: 2 - func-call-spacing: 0 - func-name-matching: 2 - func-names: 0 - func-style: 0 - generator-star-spacing: 2 - global-require: 2 - guard-for-in: 0 - handle-callback-err: 2 - id-blacklist: 0 - id-length: 0 - id-match: 2 - indent: [2, 2, {SwitchCase: 1, VariableDeclarator: {var: 2, let: 2, const: 3}}] - init-declarations: 0 - jsx-quotes: 0 - key-spacing: 0 - keyword-spacing: 2 - line-comment-position: 0 - linebreak-style: [2, 'unix'] - lines-around-comment: 0 - lines-around-directive: 2 - max-depth: 0 - max-len: 0 - max-lines: 0 - max-nested-callbacks: 0 - max-params: 0 - max-statements-per-line: 0 - max-statements: 0 - multiline-ternary: 0 - new-cap: 2 - new-parens: 2 - newline-after-var: 0 - newline-before-return: 0 - newline-per-chained-call: 0 - no-alert: 0 - no-array-constructor: 2 - no-caller: 2 - no-case-declarations: 2 - no-catch-shadow: 2 - no-class-assign: 2 - no-cond-assign: 2 - no-confusing-arrow: 2 - no-console: 0 - no-const-assign: 2 - no-constant-condition: 2 - no-control-regex: 2 - no-debugger: 2 - no-delete-var: 2 - no-div-regex: 2 - no-dupe-args: 2 - no-dupe-class-members: 2 - no-dupe-keys: 2 - no-duplicate-case: 2 - no-duplicate-imports: 2 - no-else-return: 2 - no-empty-character-class: 2 - no-empty-function: 0 - no-empty-pattern: 2 - no-empty: 2 - no-eq-null: 2 - no-eval: 2 - no-ex-assign: 2 - no-extend-native: 2 - no-extra-bind: 2 - no-extra-boolean-cast: 2 - no-extra-label: 2 - no-extra-parens: [2, 'all', {nestedBinaryExpressions: false}] - no-extra-semi: 2 - no-fallthrough: 2 - no-floating-decimal: 2 - no-func-assign: 2 - no-global-assign: 2 - no-implicit-coercion: 2 - no-implicit-globals: 2 - no-implied-eval: 2 - no-inline-comments: 0 - no-inner-declarations: [2, 'both'] - no-invalid-regexp: 2 - no-invalid-this: 2 - no-irregular-whitespace: 2 - no-iterator: 2 - no-label-var: 2 - no-labels: 2 - no-lone-blocks: 2 - no-lonely-if: 0 - no-loop-func: 2 - no-magic-numbers: 0 - no-mixed-operators: 0 - no-mixed-requires: 0 - no-mixed-spaces-and-tabs: 2 - no-multi-spaces: 0 - no-multi-str: 2 - no-multiple-empty-lines: [2, {max: 3, maxBOF: 0, maxEOF: 1}] - no-native-reassign: 2 - no-negated-condition: 0 - no-negated-in-lhs: 2 - no-nested-ternary: 2 - no-new-func: 2 - no-new-object: 2 - no-new-require: 2 - no-new-symbol: 2 - no-new-wrappers: 2 - no-new: 2 - no-obj-calls: 2 - no-octal-escape: 2 - no-octal: 2 - no-param-reassign: 2 - no-path-concat: 2 - no-plusplus: 0 - no-process-env: 2 - no-process-exit: 2 - no-proto: 2 - no-prototype-builtins: 0 - no-redeclare: 2 - no-regex-spaces: 2 - no-restricted-globals: [2, 'event'] - no-restricted-imports: 2 - no-restricted-modules: 2 - no-restricted-properties: 0 - no-restricted-syntax: 2 - no-return-assign: 0 - no-script-url: 2 - no-self-assign: 2 - no-self-compare: 2 - no-sequences: 2 - no-shadow-restricted-names: 2 - no-shadow: 2 - no-spaced-func: 2 - no-sparse-arrays: 2 - no-sync: 0 - no-tabs: 2 - no-template-curly-in-string: 2 - no-ternary: 0 - no-this-before-super: 2 - no-throw-literal: 2 - no-trailing-spaces: 2 - no-undef-init: 2 - no-undef: 2 - no-undefined: 2 - no-underscore-dangle: 0 - no-unexpected-multiline: 2 - no-unmodified-loop-condition: 2 - no-unneeded-ternary: 2 - no-unreachable: 2 - no-unsafe-finally: 2 - no-unsafe-negation: 2 - no-unused-expressions: [2, {allowShortCircuit: true, allowTernary: true}] - no-unused-labels: 2 - no-unused-vars: [2, {vars: 'all', args: 'after-used'}] - no-use-before-define: [2, 'nofunc'] - no-useless-call: 2 - no-useless-computed-key: 2 - no-useless-concat: 2 - no-useless-constructor: 2 - no-useless-escape: 2 - no-useless-rename: 2 - no-var: 2 - no-void: 2 - no-warning-comments: 0 - no-whitespace-before-property: 2 - no-with: 2 - object-curly-newline: 0 - object-curly-spacing: 0 - object-property-newline: 0 - object-shorthand: 2 - one-var-declaration-per-line: 0 - one-var: 0 - operator-assignment: [2, 'always'] - operator-linebreak: 0 - padded-blocks: 0 - prefer-arrow-callback: 2 - prefer-const: 2 - prefer-numeric-literals: 2 - prefer-reflect: 0 - prefer-rest-params: 2 - prefer-spread: 0 - prefer-template: 2 - quote-props: [2, 'as-needed'] - quotes: [2, 'single'] - radix: 2 - require-jsdoc: 0 - require-yield: 2 - rest-spread-spacing: 2 - semi-spacing: [2, {before: false, after: true}] - semi: 2 - sort-imports: 0 - sort-keys: 0 - sort-vars: 0 - space-before-blocks: [2, 'always'] - space-before-function-paren: [2, 'never'] - space-in-parens: [2, 'never'] - space-infix-ops: 2 - space-unary-ops: [2, {words: true, nonwords: false}] - spaced-comment: 0 - strict: [2, 'function'] - symbol-description: 2 - template-curly-spacing: [2, 'never'] - unicode-bom: 2 - use-isnan: 2 - valid-jsdoc: 0 - valid-typeof: 2 - vars-on-top: 2 - wrap-iife: 2 - wrap-regex: 0 - yield-star-spacing: 2 - yoda: [2, 'never'] - -overrides: - # JavaScript Files - # Disable rules which are impossible to satisfy (types require .ts extension) - - files: - - '*.js' - rules: - '@typescript-eslint/explicit-module-boundary-types': 0 - # TypeScript Files - # Some ESLint rules report false errors due to lacking type information. Replacement rules are provided for these, and the originals need to be disabled - - files: - - '*.ts' - rules: - # https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors - no-undef: 0 - no-unused-vars: 0 - '@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'after-used'}] - no-redeclare: 0 - '@typescript-eslint/no-redeclare': 2 - no-extra-parens: 0 - '@typescript-eslint/no-extra-parens': 2 - no-shadow: 0 - '@typescript-eslint/no-shadow': 2 - # Unit Tests (also written in TypeScript) - # Disable rules that do not make sense in test files (e.g. testing for undefined input values should be allowed) - - files: - - '*.spec.ts' - - 'test/*.ts' - extends: - - 'plugin:vitest/legacy-recommended' - rules: - no-undefined: 0 - no-unused-expressions: 0 - vitest/valid-expect: 0 diff --git a/assets/eslint.config.js b/assets/eslint.config.js new file mode 100644 index 000000000..166da7586 --- /dev/null +++ b/assets/eslint.config.js @@ -0,0 +1,293 @@ +import tsEslint from 'typescript-eslint'; +import vitestPlugin from 'eslint-plugin-vitest'; +import globals from 'globals'; + +export default tsEslint.config( + ...tsEslint.configs.recommended, + { + name: 'PhilomenaConfig', + files: ['**/*.js', '**/*.ts'], + languageOptions: { + ecmaVersion: 6, + sourceType: 'module', + parserOptions: { + ecmaVersion: 6, + sourceType: 'module' + }, + globals: { + ...globals.browser + } + }, + rules: { + 'accessor-pairs': 2, + 'array-bracket-spacing': 0, + 'array-callback-return': 2, + 'arrow-body-style': 0, + 'arrow-parens': [2, 'as-needed'], + 'arrow-spacing': 2, + 'block-scoped-var': 2, + 'block-spacing': 2, + 'brace-style': [2, 'stroustrup', {allowSingleLine: true}], + 'callback-return': 0, + camelcase: [2, {allow: ['camo_url', 'spoiler_image_uri', 'image_ids']}], + 'class-methods-use-this': 0, + 'comma-dangle': [2, 'only-multiline'], + 'comma-spacing': 2, + 'comma-style': 2, + complexity: 0, + 'computed-property-spacing': [2, 'never'], + 'consistent-return': 0, + 'consistent-this': [2, 'that'], + 'constructor-super': 2, + curly: [2, 'multi-line', 'consistent'], + 'default-case': 2, + 'dot-location': [2, 'property'], + 'dot-notation': [2, {allowKeywords: true}], + 'eol-last': 2, + eqeqeq: 2, + 'func-call-spacing': 0, + 'func-name-matching': 2, + 'func-names': 0, + 'func-style': 0, + 'generator-star-spacing': 2, + 'global-require': 2, + 'guard-for-in': 0, + 'handle-callback-err': 2, + 'id-blacklist': 0, + 'id-length': 0, + 'id-match': 2, + indent: [2, 2, {SwitchCase: 1, VariableDeclarator: {var: 2, let: 2, const: 3}}], + 'init-declarations': 0, + 'jsx-quotes': 0, + 'key-spacing': 0, + 'keyword-spacing': 2, + 'line-comment-position': 0, + 'linebreak-style': [2, 'unix'], + 'lines-around-comment': 0, + 'lines-around-directive': 2, + 'max-depth': 0, + 'max-len': 0, + 'max-lines': 0, + 'max-nested-callbacks': 0, + 'max-params': 0, + 'max-statements-per-line': 0, + 'max-statements': 0, + 'multiline-ternary': 0, + 'new-cap': 2, + 'new-parens': 2, + 'newline-after-var': 0, + 'newline-before-return': 0, + 'newline-per-chained-call': 0, + 'no-alert': 0, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-case-declarations': 2, + 'no-catch-shadow': 2, + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-confusing-arrow': 2, + 'no-console': 0, + 'no-const-assign': 2, + 'no-constant-condition': 2, + 'no-control-regex': 2, + 'no-debugger': 2, + 'no-delete-var': 2, + 'no-div-regex': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-duplicate-imports': 2, + 'no-else-return': 2, + 'no-empty-character-class': 2, + 'no-empty-function': 0, + 'no-empty-pattern': 2, + 'no-empty': 2, + 'no-eq-null': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-label': 2, + 'no-extra-parens': [2, 'all', {nestedBinaryExpressions: false}], + 'no-extra-semi': 2, + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-global-assign': 2, + 'no-implicit-coercion': 2, + 'no-implicit-globals': 2, + 'no-implied-eval': 2, + 'no-inline-comments': 0, + 'no-inner-declarations': [2, 'both'], + 'no-invalid-regexp': 2, + 'no-invalid-this': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': 2, + 'no-lone-blocks': 2, + 'no-lonely-if': 0, + 'no-loop-func': 2, + 'no-magic-numbers': 0, + 'no-mixed-operators': 0, + 'no-mixed-requires': 0, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 0, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, {max: 3, maxBOF: 0, maxEOF: 1}], + 'no-native-reassign': 2, + 'no-negated-condition': 0, + 'no-negated-in-lhs': 2, + 'no-nested-ternary': 2, + 'no-new-func': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-new': 2, + 'no-obj-calls': 2, + 'no-octal-escape': 2, + 'no-octal': 2, + 'no-param-reassign': 2, + 'no-path-concat': 2, + 'no-plusplus': 0, + 'no-process-env': 2, + 'no-process-exit': 2, + 'no-proto': 2, + 'no-prototype-builtins': 0, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-restricted-globals': [2, 'event'], + 'no-restricted-imports': 2, + 'no-restricted-modules': 2, + 'no-restricted-properties': 0, + 'no-restricted-syntax': 2, + 'no-return-assign': 0, + 'no-script-url': 2, + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-shadow': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-sync': 0, + 'no-tabs': 2, + 'no-template-curly-in-string': 2, + 'no-ternary': 0, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef-init': 2, + 'no-undef': 2, + 'no-undefined': 2, + 'no-underscore-dangle': 0, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': 2, + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unsafe-negation': 2, + 'no-unused-expressions': [2, {allowShortCircuit: true, allowTernary: true}], + 'no-unused-labels': 2, + 'no-unused-vars': [2, {vars: 'all', args: 'after-used', varsIgnorePattern: '^_', argsIgnorePattern: '^_'}], + 'no-use-before-define': [2, 'nofunc'], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-concat': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 2, + 'no-useless-rename': 2, + 'no-var': 2, + 'no-void': 2, + 'no-warning-comments': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'object-curly-newline': 0, + 'object-curly-spacing': 0, + 'object-property-newline': 0, + 'object-shorthand': 2, + 'one-var-declaration-per-line': 0, + 'one-var': 0, + 'operator-assignment': [2, 'always'], + 'operator-linebreak': 0, + 'padded-blocks': 0, + 'prefer-arrow-callback': 2, + 'prefer-const': 2, + 'prefer-numeric-literals': 2, + 'prefer-reflect': 0, + 'prefer-rest-params': 2, + 'prefer-spread': 0, + 'prefer-template': 2, + 'quote-props': [2, 'as-needed'], + quotes: [2, 'single'], + radix: 2, + 'require-jsdoc': 0, + 'require-yield': 2, + 'rest-spread-spacing': 2, + 'semi-spacing': [2, {before: false, after: true}], + semi: 2, + 'sort-imports': 0, + 'sort-keys': 0, + 'sort-vars': 0, + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, {words: true, nonwords: false}], + 'spaced-comment': 0, + strict: [2, 'function'], + 'symbol-description': 2, + 'template-curly-spacing': [2, 'never'], + 'unicode-bom': 2, + 'use-isnan': 2, + 'valid-jsdoc': 0, + 'valid-typeof': 2, + 'vars-on-top': 2, + 'wrap-iife': 2, + 'wrap-regex': 0, + 'yield-star-spacing': 2, + yoda: [2, 'never'], + }, + ignores: [ + 'js/vendor/*', + 'vite.config.ts' + ] + }, + { + files: ['**/*.js'], + rules: { + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/no-unused-vars': 'off' + } + }, + { + files: ['**/*.ts'], + rules: { + 'no-undef': 'off', + 'no-unused-vars': 'off', + 'no-redeclare': 'off', + 'no-shadow': 'off', + '@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'after-used', varsIgnorePattern: '^_.*', argsIgnorePattern: '^_.*'}], + '@typescript-eslint/no-redeclare': 2, + '@typescript-eslint/no-shadow': 2 + } + }, + { + files: ['**/*.spec.ts', '**/test/*.ts'], + plugins: { + vitest: vitestPlugin + }, + rules: { + ...vitestPlugin.configs.recommended.rules, + 'no-undef': 'off', + 'no-undefined': 'off', + 'no-unused-expressions': 0, + 'vitest/valid-expect': 0, + '@typescript-eslint/no-unused-expressions': 0 + } + } +); diff --git a/assets/js/booru.js b/assets/js/booru.js index 1f963dbe0..3e1e7dacf 100644 --- a/assets/js/booru.js +++ b/assets/js/booru.js @@ -122,7 +122,7 @@ function initializeFilters() { function unmarshal(data) { try { return JSON.parse(data); } - catch (_) { return data; } + catch { return data; } } function loadBooruData() { diff --git a/assets/js/fp.ts b/assets/js/fp.ts index 49182bc90..631cc26fd 100644 --- a/assets/js/fp.ts +++ b/assets/js/fp.ts @@ -152,7 +152,7 @@ export async function setSesCookie() { fp = `d${await createFp()}`; } // If it fails, use fakeprint "d015c342859dde3" as a last resort. - catch (err) { + catch { fp = 'd015c342859dde3'; } diff --git a/assets/js/imagesclientside.ts b/assets/js/imagesclientside.ts index 47fef0e9b..de0b7727b 100644 --- a/assets/js/imagesclientside.ts +++ b/assets/js/imagesclientside.ts @@ -7,6 +7,7 @@ import { setupInteractions } from './interactions'; import { showThumb, showBlock, spoilerThumb, spoilerBlock, hideThumb } from './utils/image'; import { TagData, getHiddenTags, getSpoileredTags, imageHitsTags, imageHitsComplex, displayTags } from './utils/tag'; import { AstMatcher } from './query/types'; +import { assertNotUndefined } from './utils/assert'; type RunFilterCallback = (img: HTMLDivElement, test: TagData[]) => void; @@ -16,7 +17,7 @@ function runFilter(img: HTMLDivElement, test: TagData[] | boolean, runCallback: runCallback(img, test as TagData[]); // I don't like this. - img.dataset.imageId && window.booru.imagesWithDownvotingDisabled.push(img.dataset.imageId); + window.booru.imagesWithDownvotingDisabled.push(assertNotUndefined(img.dataset.imageId)); return true; } diff --git a/assets/js/search.ts b/assets/js/search.ts index 068ff068f..7de65e5aa 100644 --- a/assets/js/search.ts +++ b/assets/js/search.ts @@ -37,14 +37,14 @@ function executeFormHelper(e: PointerEvent) { const searchField = $('.js-search-field'); const attr = (name: string) => e.target && (e.target as HTMLElement).getAttribute(name); - attr('data-search-add') && addTag(searchField, attr('data-search-add')); - attr('data-search-show-help') && showHelp((e.target as Node).textContent || '', attr('data-search-show-help')); - attr('data-search-select-last') && searchField && selectLast(searchField, parseInt(attr('data-search-select-last') || '', 10)); - attr('data-search-prepend') && searchField && prependToLast(searchField, attr('data-search-prepend') || ''); + if (attr('data-search-add')) addTag(searchField, attr('data-search-add')); + if (attr('data-search-show-help')) showHelp((e.target as Node).textContent || '', attr('data-search-show-help')); + if (attr('data-search-select-last') && searchField) selectLast(searchField, parseInt(attr('data-search-select-last') || '', 10)); + if (attr('data-search-prepend') && searchField) prependToLast(searchField, attr('data-search-prepend') || ''); } export function setupSearch() { const form = $('.js-search-form'); - form && form.addEventListener('click', executeFormHelper as EventListener); + if (form) form.addEventListener('click', executeFormHelper as EventListener); } diff --git a/assets/js/settings.ts b/assets/js/settings.ts index d6b71d449..db5c8e6e1 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -20,9 +20,11 @@ export function setupSettings() { }); // Theme preview - themeSelect && themeSelect.addEventListener('change', () => { - if (styleSheet) { - styleSheet.href = themeSelect.options[themeSelect.selectedIndex].dataset.themePath || '#'; - } - }); + if (themeSelect) { + themeSelect.addEventListener('change', () => { + if (styleSheet) { + styleSheet.href = themeSelect.options[themeSelect.selectedIndex].dataset.themePath || '#'; + } + }); + } } diff --git a/assets/js/shortcuts.ts b/assets/js/shortcuts.ts index c76a04585..f648724d6 100644 --- a/assets/js/shortcuts.ts +++ b/assets/js/shortcuts.ts @@ -55,11 +55,12 @@ const keyCodes: ShortcutKeycodes = { KeyO() { openFullView(); }, // O - open original KeyV() { openFullViewNewTab(); }, // V - open original in a new tab KeyF() { // F - favourite image - getHover() ? click(`a.interaction--fave[data-image-id="${getHover()}"]`) + /* Gotta use a "return" here and in the next function because eslint is silly */ + return getHover() ? click(`a.interaction--fave[data-image-id="${getHover()}"]`) : click('.block__header a.interaction--fave'); }, KeyU() { // U - upvote image - getHover() ? click(`a.interaction--upvote[data-image-id="${getHover()}"]`) + return getHover() ? click(`a.interaction--upvote[data-image-id="${getHover()}"]`) : click('.block__header a.interaction--upvote'); }, }; diff --git a/assets/js/tagsmisc.ts b/assets/js/tagsmisc.ts index e9882baed..b68ea8b3e 100644 --- a/assets/js/tagsmisc.ts +++ b/assets/js/tagsmisc.ts @@ -18,7 +18,7 @@ type TagInputActionList = { function tagInputButtons({target}: PointerEvent) { const actions: TagInputActionList = { save(tagInput: HTMLTextAreaElement | null) { - tagInput && store.set('tag_input', tagInput.value); + if (tagInput) store.set('tag_input', tagInput.value); }, load(tagInput: HTMLTextAreaElement | null) { if (!tagInput) { return; } diff --git a/assets/js/utils/store.ts b/assets/js/utils/store.ts index 212509460..c09a09cee 100644 --- a/assets/js/utils/store.ts +++ b/assets/js/utils/store.ts @@ -11,7 +11,7 @@ export default { localStorage.setItem(key, JSON.stringify(value)); return true; } - catch (err) { + catch { return false; } }, @@ -22,7 +22,7 @@ export default { try { return JSON.parse(value); } - catch (err) { + catch { return value as unknown as Value; } }, @@ -32,7 +32,7 @@ export default { localStorage.removeItem(key); return true; } - catch (err) { + catch { return false; } }, @@ -51,7 +51,7 @@ export default { const lastUpdatedKey = key + lastUpdatedSuffix; const lastUpdatedTime = Date.now() + maxAge; - this.set(key, value) && this.set(lastUpdatedKey, lastUpdatedTime); + return this.set(key, value) && this.set(lastUpdatedKey, lastUpdatedTime); }, // Whether the value of a key set with setWithExpireTime() has expired diff --git a/assets/package-lock.json b/assets/package-lock.json index 07f86d295..5312c4905 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -9,16 +9,15 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@types/postcss-mixins": "^9.0.5", "@types/web": "^0.0.148", - "@typescript-eslint/eslint-plugin": "^7.13.0", - "@typescript-eslint/parser": "^7.13.0", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "jest-environment-jsdom": "^29.7.0", "normalize.css": "^8.0.1", "postcss-mixins": "^10.0.1", "postcss-simple-vars": "^7.0.1", "typescript": "^5.4", + "typescript-eslint": "8.0.0-alpha.30", "vite": "^5.2" }, "devDependencies": { @@ -799,6 +798,17 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", @@ -807,15 +817,28 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.15.1.tgz", + "integrity": "sha512-K4gzNq+yymn/EVsXYmf+SBcBro8MTf+aXJZUphM96CdzUEr+ClGDvAbpmaEK+cGVigVXIgs9gNmvHAlrzzY5JQ==", + "dependencies": { + "@eslint/object-schema": "^2.1.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -823,38 +846,26 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/@eslint/js": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.4.0.tgz", + "integrity": "sha512-fdI7VJjP3Rvc70lC4xkFXHB0fiPeojiL1PxVG6t1ZvXQrarj893PweuBTujxDUFk0Fxj4R7PIIAZ/aiiyZPZcg==", "engines": { - "node": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@fortawesome/fontawesome-free": { @@ -866,40 +877,6 @@ "node": ">=6" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -912,11 +889,17 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead" + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", @@ -1470,30 +1453,30 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.0.tgz", - "integrity": "sha512-FX1X6AF0w8MdVFLSdqwqN/me2hyhuQg4ykN6ZpVhh1ij/80pTvDKclX1sZB9iqex8SjQfVhwMKs3JtnnMLzG9w==", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.30.tgz", + "integrity": "sha512-2CBUupdkfbE3eATph4QeZejvT+M+1bVur+zXlVx09WN31phap51ps/qemeclnCbGEz6kTgBDmScrr9XmmF8/Pg==", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/type-utils": "7.13.0", - "@typescript-eslint/utils": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/scope-manager": "8.0.0-alpha.30", + "@typescript-eslint/type-utils": "8.0.0-alpha.30", + "@typescript-eslint/utils": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1502,25 +1485,25 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.0.tgz", - "integrity": "sha512-EjMfl69KOS9awXXe83iRN7oIEXy9yYdqWfqdrFAYAAr6syP8eLEFI7ZE4939antx2mNgPRW/o1ybm2SFYkbTVA==", - "dependencies": { - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.30.tgz", + "integrity": "sha512-tAYgFmgXU1MlCK3nbblUvJlDSibBvxtAQXGrF3IG0KmnRza9FXILZifHWL0rrwacDn40K53K607Fk2QkMjiGgw==", + "dependencies": { + "@typescript-eslint/scope-manager": "8.0.0-alpha.30", + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1529,15 +1512,15 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz", - "integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.30.tgz", + "integrity": "sha512-FGW/iPWGyPFamAVZ60oCAthMqQrqafUGebF8UKuq/ha+e9SVG6YhJoRzurlQXOVf8dHfOhJ0ADMXyFnMc53clg==", "dependencies": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0" + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1545,25 +1528,22 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.0.tgz", - "integrity": "sha512-xMEtMzxq9eRkZy48XuxlBFzpVMDurUAfDu5Rz16GouAtXm0TaAoTFzqWUFPPuQYXI/CDaH/Bgx/fk/84t/Bc9A==", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.30.tgz", + "integrity": "sha512-FrnhlCKEKZKRbpDviHkIU9tayIUGTOfa+SjvrRv6p/AJIUv6QT8oRboRjLH/cCuwUEbM0k5UtRWYug4albHUqQ==", "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.0", - "@typescript-eslint/utils": "7.13.0", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.30", + "@typescript-eslint/utils": "8.0.0-alpha.30", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^8.56.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -1571,11 +1551,11 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz", - "integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.30.tgz", + "integrity": "sha512-4WzLlw27SO9pK9UFj/Hu7WGo8WveT0SEiIpFVsV2WwtQmLps6kouwtVCB8GJPZKJyurhZhcqCoQVQFmpv441Vg==", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1583,12 +1563,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz", - "integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.30.tgz", + "integrity": "sha512-WSXbc9ZcXI+7yC+6q95u77i8FXz6HOLsw3ST+vMUlFy1lFbXyFL/3e6HDKQCm2Clt0krnoCPiTGvIn+GkYPn4Q==", "dependencies": { - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/visitor-keys": "7.13.0", + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1597,7 +1577,7 @@ "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1609,47 +1589,75 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz", - "integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.30.tgz", + "integrity": "sha512-rfhqfLqFyXhHNDwMnHiVGxl/Z2q/3guQ1jLlGQ0hi9Rb7inmwz42crM+NnLPR+2vEnwyw1P/g7fnQgQ3qvFx4g==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.0", - "@typescript-eslint/types": "7.13.0", - "@typescript-eslint/typescript-estree": "7.13.0" + "@typescript-eslint/scope-manager": "8.0.0-alpha.30", + "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.30" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz", - "integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==", + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.30.tgz", + "integrity": "sha512-XZuNurZxBqmr6ZIRIwWFq7j5RZd6ZlkId/HZEWyfciK+CWoyOxSF9Pv2VXH9Rlu2ZG2PfbhLz2Veszl4Pfn7yA==", "dependencies": { - "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/types": "8.0.0-alpha.30", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, "node_modules/@vitest/coverage-v8": { "version": "1.6.0", @@ -2094,11 +2102,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -2498,17 +2507,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -2644,40 +2642,36 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.4.0.tgz", + "integrity": "sha512-sjc7Y8cUD1IlwYcTS9qPSvGjAC8Ne9LctpxKKu3x/1IC9bnOg98Zy6GxEJUfr1NojMgVPlyANXYns8oE2c1TAA==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/config-array": "^0.15.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.4.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -2691,7 +2685,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2721,25 +2715,117 @@ } } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.0.tgz", + "integrity": "sha512-ZrMCe1R6a01T94ilV13egvcnvVJ1pxShkE0+NDjDzH4nvG1wXpwsVI5bZCvE7AEDH1mXEx5tJSVR68bLgG7Dng==", + "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-visitor-keys": { + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.0.tgz", + "integrity": "sha512-QWuwm9wcGMAuTsxP+qz6LBBd3Uq8I5Nv8xb0mk54jmNoCyDspnMvVsOxI6IsMmway5d1S9Su2+sCKv1st2l6eA==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.0.tgz", + "integrity": "sha512-cAvBvUoobaoIcoqox1YatXOnSl3gx92rCZoMRPzMNisDiM12siGilSM4+dJAekuuHTibI2hVC2fYK79iSFvWjw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/visitor-keys": "7.13.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.0.tgz", + "integrity": "sha512-jceD8RgdKORVnB4Y6BqasfIkFhl4pajB1wVxrF4akxD2QPM8GNYjgGwEzYS+437ewlqqrg7Dw+6dhdpjMpeBFQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.13.0", + "@typescript-eslint/types": "7.13.0", + "@typescript-eslint/typescript-estree": "7.13.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.0.tgz", + "integrity": "sha512-nxn+dozQx+MK61nn/JP+M4eCkHDSxSLDpgE3WcQo0+fkjEolnaB5jswvIKC4K56By8MMgIho7f1PVxERHEo8rw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -2747,37 +2833,58 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/eslint-plugin-vitest/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/eslint-scope": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", "dependencies": { - "brace-expansion": "^1.1.7" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.11.3", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2930,14 +3037,14 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -2967,16 +3074,15 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -3012,7 +3118,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -3053,6 +3160,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3079,26 +3187,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -3138,14 +3226,11 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3315,6 +3400,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3323,7 +3409,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "1.3.8", @@ -4059,17 +4146,14 @@ } }, "node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/mlly": { @@ -4216,6 +4300,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -4331,6 +4416,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -4684,21 +4770,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/rollup": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", @@ -5251,28 +5322,6 @@ "node": ">=8" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5378,17 +5427,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", @@ -5401,6 +5439,28 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.0.0-alpha.30", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.30.tgz", + "integrity": "sha512-/vGhBMsK1TpadQh1eQ02c5pyiPGmKR9cVzX5C9plZ+LC0HPLpWoJbbTVfQN7BkIK7tUxDt2BFr3pFL5hDDrx7g==", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.30", + "@typescript-eslint/parser": "8.0.0-alpha.30", + "@typescript-eslint/utils": "8.0.0-alpha.30" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/ufo": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", @@ -5792,7 +5852,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "5.0.1", diff --git a/assets/package.json b/assets/package.json index de267bd4e..2bae17fee 100644 --- a/assets/package.json +++ b/assets/package.json @@ -2,7 +2,7 @@ "type": "module", "scripts": { "deploy": "cross-env NODE_ENV=production tsc && cross-env NODE_ENV=production vite build", - "lint": "eslint . --ext .js,.ts && stylelint ./css/**/*.css", + "lint": "eslint . && stylelint ./css/**/*.css", "test": "vitest run --coverage", "test:watch": "vitest watch --coverage", "dev": "vite", @@ -14,11 +14,10 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@types/postcss-mixins": "^9.0.5", "@types/web": "^0.0.148", - "@typescript-eslint/eslint-plugin": "^7.13.0", - "@typescript-eslint/parser": "^7.13.0", + "typescript-eslint": "8.0.0-alpha.30", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "jest-environment-jsdom": "^29.7.0", "normalize.css": "^8.0.1", "postcss-mixins": "^10.0.1", diff --git a/assets/test/vitest-setup.ts b/assets/test/vitest-setup.ts index 446beac6e..a8d458745 100644 --- a/assets/test/vitest-setup.ts +++ b/assets/test/vitest-setup.ts @@ -5,7 +5,6 @@ import { Blob } from 'node:buffer'; import { fireEvent } from '@testing-library/dom'; window.booru = { - // eslint-disable-next-line @typescript-eslint/no-empty-function timeAgo: () => {}, csrfToken: 'mockCsrfToken', hiddenTag: '/mock-tagblocked.svg', From c2c4a0b943909a78894d9fc0c9d56b2544481671 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Thu, 13 Jun 2024 00:01:15 +0200 Subject: [PATCH 43/65] profile page appearance --- assets/css/elements/block.css | 4 +- .../templates/profile/_about_me.html.slime | 28 ++--- .../templates/profile/_commission.html.slime | 4 + .../templates/profile/show.html.slime | 119 +++++++++++------- 4 files changed, 94 insertions(+), 61 deletions(-) diff --git a/assets/css/elements/block.css b/assets/css/elements/block.css index ad373336a..ec9b8a69d 100644 --- a/assets/css/elements/block.css +++ b/assets/css/elements/block.css @@ -104,7 +104,9 @@ } .block__header > .fa, -.block__header--single-item > .fa { +.block__header--single-item > .fa, +.block__header > span .fa, +.block__header--single-item > span .fa { margin-right: var(--padding-normal); } diff --git a/lib/philomena_web/templates/profile/_about_me.html.slime b/lib/philomena_web/templates/profile/_about_me.html.slime index 7be031fb1..72a283d05 100644 --- a/lib/philomena_web/templates/profile/_about_me.html.slime +++ b/lib/philomena_web/templates/profile/_about_me.html.slime @@ -1,19 +1,15 @@ .block__content.profile-about - = cond do - - @user.description not in [nil, ""] -> - = @about_me + p + = cond do + - @user.description not in [nil, ""] -> + = @about_me - - current?(@user, @conn.assigns.current_user) -> - em - ' Want to - => link "add some info about yourself?", to: ~p"/profiles/#{@user}/description/edit" + - current?(@user, @conn.assigns.current_user) -> + span: i.fa.icon--padded--right.fa-info-circle + em + ' Want to + => link "add some info about yourself?", to: ~p"/profiles/#{@user}/description/edit" - - true -> - | No description provided. - - = if can?(@conn, :edit_description, @user) do - = if @user.description not in [nil, ""] do - hr.separator.separator--secondary - a.button href=~p"/profiles/#{@user}/description/edit" - i.fa.fa-edit> - | Edit + - true -> + span: i.fa.icon--padded--right.fa-info-circle + | No description provided. diff --git a/lib/philomena_web/templates/profile/_commission.html.slime b/lib/philomena_web/templates/profile/_commission.html.slime index be102740d..25ba67d92 100644 --- a/lib/philomena_web/templates/profile/_commission.html.slime +++ b/lib/philomena_web/templates/profile/_commission.html.slime @@ -24,6 +24,7 @@ = link "More information", to: ~p"/profiles/#{@user}/commission" - current?(@user, @conn.assigns.current_user) -> + span: i.fa.icon--padded--right.fa-info-circle = if Enum.any?(@conn.assigns.user.verified_links) do em ' You don't have any commission information listed yet. @@ -36,3 +37,6 @@ ' to set one up. - true -> + p + span: i.fa.icon--padded--right.fa-info-circle + | No commission listing set up diff --git a/lib/philomena_web/templates/profile/show.html.slime b/lib/philomena_web/templates/profile/show.html.slime index 81f68c3c6..73a6ea6c8 100644 --- a/lib/philomena_web/templates/profile/show.html.slime +++ b/lib/philomena_web/templates/profile/show.html.slime @@ -66,9 +66,15 @@ = render PhilomenaWeb.ProfileView, "_commission.html", user: @user, commission_information: @commission_information, conn: @conn .block - .block__header - i.fa.fa-link - | Artist Links + .block__header.flex + span + i.fa.fa-link + | Artist Links + = if current?(@user, @conn.assigns.current_user) or manages_links?(@conn, @user) do + .block__header__buttons + a.button href=~p"/profiles/#{@user}/artist_links/new" + i.fa.fa-link> + | Create = for link <- @user.verified_links, should_see_link?(@conn, @user, link) do - watchers = if link.tag, do: @watcher_counts[link.tag.id] || 0, else: 0 @@ -101,16 +107,22 @@ => unless link.public do br ' Hidden + = if Enum.count(@user.verified_links) == 0 do + .block__content + p + span: i.fa.icon--padded--right.fa-info-circle + | No artist links associated - = if current?(@user, @conn.assigns.current_user) or manages_links?(@conn, @user) do - .block__content.alternating-color.break-word - a.button href=~p"/profiles/#{@user}/artist_links/new" - i.fa.fa-link> - | Create an Artist Link .block - .block__header - i.fa.fa-award - | Badges + .block__header.flex + span + i.fa.fa-award + | Badges + = if manages_awards?(@conn) and not hide_staff_tools?(@conn) do + .block__header__buttons + a.button href=~p"/profiles/#{@user}/awards/new" + i.fa.fa-trophy> + | Award = for award <- award_order(@user.awards) do .block__content.flex.flex--centered.flex--center-distributed.alternating-color.no-overflow title=award.label .flex__grow.center @@ -129,49 +141,68 @@ br a href=~p"/profiles/#{@user}/awards/#{award}/edit" ' Edit - = if manages_awards?(@conn) and not hide_staff_tools?(@conn) do - .block__content.alternating-color.no-overflow - a.button href=~p"/profiles/#{@user}/awards/new" - i.fa.fa-trophy> - | Award a badge - + = if Enum.count(@user.awards) == 0 do + .block__content + p + span: i.fa.icon--padded--right.fa-info-circle + | No badge awards received .block - .block__header - i.fa.fa-question - | About Me + .block__header.flex + span + i.fa.fa-question + | About Me + = if can?(@conn, :edit_description, @user) do + .block__header__buttons + a.button href=~p"/profiles/#{@user}/description/edit" + i.fa.fa-edit> + | Edit = render PhilomenaWeb.ProfileView, "_about_me.html", user: @user, about_me: @about_me, conn: @conn = if can_read_mod_notes?(@conn) and not hide_staff_tools?(@conn) do .block.block--warning - .block__header - i.fa.fa-sticky-note - | Mod Notes - table.table - thead - tr - th Note - th Created - tbody - = for {body, mod_note} <- @mod_notes do - tr - td = body - td = pretty_time(mod_note.created_at) + .block__header.flex + span + i.fa.fa-sticky-note + | Mod Notes + .block__header__buttons + a.button href=~p"/profiles/#{@user}/details" + i.fa.fa-plus> + | Edit .block__content - a.button href=~p"/profiles/#{@user}/details" - i.fa.fa-plus> - | Edit mod notes + = if Enum.count(@mod_notes) > 0 do + table.table + thead + tr + th Note + th Created + tbody + = for {body, mod_note} <- @mod_notes do + tr + td = body + td = pretty_time(mod_note.created_at) + - else + p + span: i.fa.icon--padded--right.fa-info-circle + | No mod notes = if can_index_user?(@conn) do .block.block--warning - .block__header - i.fa.fa-clipboard - | Moderation Scratchpad + .block__header.flex + span + i.fa.fa-clipboard + | Moderation Scratchpad + .block__header__buttons + a.button href=~p"/profiles/#{@user}/scratchpad/edit" + i.fa.fa-edit> + | Edit .block__content.profile-about - = @scratchpad - .block__content - a.button href=~p"/profiles/#{@user}/scratchpad/edit" - i.fa.fa-edit> - | Edit + - {_, contents} = @scratchpad + = if String.trim(contents) != "" do + = @scratchpad + - else + p + span: i.fa.icon--padded--right.fa-info-circle + | No information present .column-layout__main = render PhilomenaWeb.ProfileView, "_statistics.html", user: @user, statistics: @statistics, conn: @conn From 2edc80bd7c4b91c0b0524e1c0f39db00cfca1582 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Thu, 13 Jun 2024 00:22:58 +0200 Subject: [PATCH 44/65] fix minor spelling errors --- lib/philomena_web/templates/profile/_about_me.html.slime | 2 +- lib/philomena_web/templates/profile/show.html.slime | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/philomena_web/templates/profile/_about_me.html.slime b/lib/philomena_web/templates/profile/_about_me.html.slime index 72a283d05..49b1018c1 100644 --- a/lib/philomena_web/templates/profile/_about_me.html.slime +++ b/lib/philomena_web/templates/profile/_about_me.html.slime @@ -12,4 +12,4 @@ - true -> span: i.fa.icon--padded--right.fa-info-circle - | No description provided. + | No description provided diff --git a/lib/philomena_web/templates/profile/show.html.slime b/lib/philomena_web/templates/profile/show.html.slime index 73a6ea6c8..1eed7c65f 100644 --- a/lib/philomena_web/templates/profile/show.html.slime +++ b/lib/philomena_web/templates/profile/show.html.slime @@ -168,7 +168,7 @@ .block__header__buttons a.button href=~p"/profiles/#{@user}/details" i.fa.fa-plus> - | Edit + | Add / Edit .block__content = if Enum.count(@mod_notes) > 0 do table.table From 2bf13011cc304e93b981c0b22a5cddaef2273af5 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Thu, 13 Jun 2024 00:27:08 +0200 Subject: [PATCH 45/65] you know what, let's make tests pass --- .../controllers/confirmation_controller_test.exs | 10 +++++----- .../controllers/password_controller_test.exs | 8 ++++---- .../controllers/registration/email_controller_test.exs | 6 +++--- .../controllers/session_controller_test.exs | 4 ++-- .../controllers/unlock_controller_test.exs | 10 +++++----- test/philomena_web/user_auth_test.exs | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/philomena_web/controllers/confirmation_controller_test.exs b/test/philomena_web/controllers/confirmation_controller_test.exs index 049fd6d1f..cc2a8ae42 100644 --- a/test/philomena_web/controllers/confirmation_controller_test.exs +++ b/test/philomena_web/controllers/confirmation_controller_test.exs @@ -27,7 +27,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" assert Repo.get_by!(Users.UserToken, user_id: user.id).context == "confirm" end @@ -40,7 +40,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" refute Repo.get_by(Users.UserToken, user_id: user.id) end @@ -51,7 +51,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" assert Repo.all(Users.UserToken) == [] end end @@ -73,7 +73,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do conn = get(conn, ~p"/confirmations/#{token}") assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :error) =~ + assert Flash.get(conn.assigns.flash, :warning) =~ "Confirmation link is invalid or it has expired" end @@ -81,7 +81,7 @@ defmodule PhilomenaWeb.ConfirmationControllerTest do conn = get(conn, ~p"/confirmations/oops") assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :error) =~ + assert Flash.get(conn.assigns.flash, :warning) =~ "Confirmation link is invalid or it has expired" refute Users.get_user!(user.id).confirmed_at diff --git a/test/philomena_web/controllers/password_controller_test.exs b/test/philomena_web/controllers/password_controller_test.exs index b74cf0462..e7f90ed5b 100644 --- a/test/philomena_web/controllers/password_controller_test.exs +++ b/test/philomena_web/controllers/password_controller_test.exs @@ -26,7 +26,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" assert Repo.get_by!(Users.UserToken, user_id: user.id).context == "reset_password" end @@ -37,7 +37,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" assert Repo.all(Users.UserToken) == [] end end @@ -61,7 +61,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do conn = get(conn, ~p"/passwords/oops/edit") assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :error) =~ + assert Flash.get(conn.assigns.flash, :warning) =~ "Reset password link is invalid or it has expired" end end @@ -109,7 +109,7 @@ defmodule PhilomenaWeb.PasswordControllerTest do conn = put(conn, ~p"/passwords/oops") assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :error) =~ + assert Flash.get(conn.assigns.flash, :warning) =~ "Reset password link is invalid or it has expired" end end diff --git a/test/philomena_web/controllers/registration/email_controller_test.exs b/test/philomena_web/controllers/registration/email_controller_test.exs index 756fea2e4..157a7f9f8 100644 --- a/test/philomena_web/controllers/registration/email_controller_test.exs +++ b/test/philomena_web/controllers/registration/email_controller_test.exs @@ -17,7 +17,7 @@ defmodule PhilomenaWeb.Registration.EmailControllerTest do }) assert redirected_to(conn) == ~p"/registrations/edit" - assert Flash.get(conn.assigns.flash, :info) =~ "A link to confirm your email" + assert Flash.get(conn.assigns.flash, :alert) =~ "A link to confirm your email" assert Users.get_user_by_email(user.email) end @@ -54,7 +54,7 @@ defmodule PhilomenaWeb.Registration.EmailControllerTest do conn = get(conn, ~p"/registrations/email/#{token}") assert redirected_to(conn) == ~p"/registrations/edit" - assert Flash.get(conn.assigns.flash, :error) =~ + assert Flash.get(conn.assigns.flash, :warning) =~ "Email change link is invalid or it has expired" end @@ -62,7 +62,7 @@ defmodule PhilomenaWeb.Registration.EmailControllerTest do conn = get(conn, ~p"/registrations/email/oops") assert redirected_to(conn) == ~p"/registrations/edit" - assert Flash.get(conn.assigns.flash, :error) =~ + assert Flash.get(conn.assigns.flash, :warning) =~ "Email change link is invalid or it has expired" assert Users.get_user_by_email(user.email) diff --git a/test/philomena_web/controllers/session_controller_test.exs b/test/philomena_web/controllers/session_controller_test.exs index 29d8d7f2d..6aec27932 100644 --- a/test/philomena_web/controllers/session_controller_test.exs +++ b/test/philomena_web/controllers/session_controller_test.exs @@ -33,8 +33,8 @@ defmodule PhilomenaWeb.SessionControllerTest do conn = get(conn, "/registrations/edit") response = html_response(conn, 200) assert response =~ user.email - assert response =~ "Settings" - assert response =~ "Logout" + assert response =~ "Settings" + assert response =~ "Logout" end test "logs the user in with remember me", %{conn: conn, user: user} do diff --git a/test/philomena_web/controllers/unlock_controller_test.exs b/test/philomena_web/controllers/unlock_controller_test.exs index bc8df85e4..43a251e78 100644 --- a/test/philomena_web/controllers/unlock_controller_test.exs +++ b/test/philomena_web/controllers/unlock_controller_test.exs @@ -27,7 +27,7 @@ defmodule PhilomenaWeb.UnlockControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" assert Repo.get_by!(Users.UserToken, user_id: user.id).context == "unlock" end @@ -40,7 +40,7 @@ defmodule PhilomenaWeb.UnlockControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" refute Repo.get_by(Users.UserToken, user_id: user.id) end @@ -51,7 +51,7 @@ defmodule PhilomenaWeb.UnlockControllerTest do }) assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :info) =~ "If your email is in our system" + assert Flash.get(conn.assigns.flash, :alert) =~ "If your email is in our system" assert Repo.all(Users.UserToken) == [] end end @@ -72,13 +72,13 @@ defmodule PhilomenaWeb.UnlockControllerTest do conn = get(conn, ~p"/unlocks/#{token}") assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :error) =~ "Unlock link is invalid or it has expired" + assert Flash.get(conn.assigns.flash, :warning) =~ "Unlock link is invalid or it has expired" end test "does not unlock with invalid token", %{conn: conn, user: user} do conn = get(conn, ~p"/unlocks/oops") assert redirected_to(conn) == "/" - assert Flash.get(conn.assigns.flash, :error) =~ "Unlock link is invalid or it has expired" + assert Flash.get(conn.assigns.flash, :warning) =~ "Unlock link is invalid or it has expired" assert Users.get_user!(user.id).locked_at end end diff --git a/test/philomena_web/user_auth_test.exs b/test/philomena_web/user_auth_test.exs index deeaeca7f..b66e5046a 100644 --- a/test/philomena_web/user_auth_test.exs +++ b/test/philomena_web/user_auth_test.exs @@ -164,7 +164,7 @@ defmodule PhilomenaWeb.UserAuthTest do assert conn.halted assert redirected_to(conn) == ~p"/sessions/new" - assert Phoenix.Flash.get(conn.assigns.flash, :error) == + assert Phoenix.Flash.get(conn.assigns.flash, :warning) == "You must log in to access this page." end From d96bcb273cfa26110e84ca0b138eb858ca3ba29f Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Sun, 23 Jun 2024 14:38:43 +0200 Subject: [PATCH 46/65] clean up now unused type --- assets/js/sources.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/assets/js/sources.ts b/assets/js/sources.ts index 1eb0afece..b4bc51ecf 100644 --- a/assets/js/sources.ts +++ b/assets/js/sources.ts @@ -3,10 +3,6 @@ import { $ } from './utils/dom'; import { inputDuplicatorCreator } from './input-duplicator'; import '../types/ujs'; -export interface TagSourceEvent extends CustomEvent { - target: HTMLElement, -} - function setupInputs() { inputDuplicatorCreator({ addButtonSelector: '.js-image-add-source', From 952993de10af9f750fa9b15071827a2fd71ef254 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Sun, 23 Jun 2024 14:38:56 +0200 Subject: [PATCH 47/65] convert burger menu logic to typescript --- assets/js/{burger.js => burger.ts} | 37 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) rename assets/js/{burger.js => burger.ts} (53%) diff --git a/assets/js/burger.js b/assets/js/burger.ts similarity index 53% rename from assets/js/burger.js rename to assets/js/burger.ts index c7a573f4f..4801e3422 100644 --- a/assets/js/burger.js +++ b/assets/js/burger.ts @@ -2,12 +2,15 @@ * Hamburger menu. */ -function switchClasses(element, oldClass, newClass) { +import { $, $$ } from './utils/dom'; +import { assertNotNull } from './utils/assert'; + +function switchClasses(element: HTMLElement, oldClass: string, newClass: string) { element.classList.remove(oldClass); element.classList.add(newClass); } -function open(burger, content, body, root) { +function open(burger: HTMLElement, content: HTMLElement, body: HTMLElement, root: HTMLElement) { switchClasses(content, 'close', 'open'); switchClasses(burger, 'close', 'open'); @@ -15,7 +18,7 @@ function open(burger, content, body, root) { body.classList.add('no-overflow'); } -function close(burger, content, body, root) { +function close(burger: HTMLElement, content: HTMLElement, body: HTMLElement, root: HTMLElement) { switchClasses(content, 'open', 'close'); switchClasses(burger, 'open', 'close'); @@ -26,27 +29,28 @@ function close(burger, content, body, root) { }, 300); } -function copyArtistLinksTo(burger) { - const copy = links => { +function copyArtistLinksTo(burger: HTMLElement) { + const copy = (links: HTMLCollection) => { burger.appendChild(document.createElement('hr')); - [].slice.call(links).forEach(link => { - const burgerLink = link.cloneNode(true); + for (let i = 0; i < links.length; i++) { + // If we're here, this should not be null. + const link = assertNotNull(links.item(i)); + const burgerLink = link.cloneNode(true) as HTMLElement; burgerLink.className = ''; burger.appendChild(burgerLink); - }); + } }; - const linksContainers = document.querySelectorAll('.js-burger-links'); - - [].slice.call(linksContainers).forEach(container => copy(container.children)); + $$('.js-burger-links').forEach(container => copy(container.children)); } -function setupBurgerMenu() { - const burger = document.getElementById('burger'); - const toggle = document.getElementById('js-burger-toggle'); - const content = document.getElementById('container'); +export function setupBurgerMenu() { + // Burger menu should exist on all pages. + const burger = assertNotNull($('#burger')); + const toggle = assertNotNull($('#js-burger-toggle')); + const content = assertNotNull($('#container')); const body = document.body; const root = document.documentElement; @@ -63,11 +67,10 @@ function setupBurgerMenu() { open(burger, content, body, root); } }); + content.addEventListener('click', () => { if (content.classList.contains('open')) { close(burger, content, body, root); } }); } - -export { setupBurgerMenu }; From c8408376cc6de77302fa5b1381003643d52e55f3 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Sun, 23 Jun 2024 14:51:40 +0200 Subject: [PATCH 48/65] move post top options into a partial --- .../templates/post/_post.html.slime | 16 +--------------- .../templates/post/_post_options_top.html.slime | 13 +++++++++++++ 2 files changed, 14 insertions(+), 15 deletions(-) create mode 100644 lib/philomena_web/templates/post/_post_options_top.html.slime diff --git a/lib/philomena_web/templates/post/_post.html.slime b/lib/philomena_web/templates/post/_post.html.slime index ff516044e..4fc346802 100644 --- a/lib/philomena_web/templates/post/_post.html.slime +++ b/lib/philomena_web/templates/post/_post.html.slime @@ -1,18 +1,4 @@ -elixir: - post_link = ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}" - -- options = if true do - .flex__spacer.hidden--desktop - a.communication__interaction.hidden--desktop title="Link to post" href="#{post_link}" - i.fa.fa-link - = if not is_nil(@post.edited_at) and can?(@conn, :show, @post) do - a href=~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/history" - ' Edited - => pretty_time(@post.edited_at) - - = if @post.edit_reason not in [nil, ""] do - ' because: - => @post.edit_reason +- options = render PhilomenaWeb.PostView, "_post_options_top.html", conn: @conn, post: @post article.block.communication id="post_#{@post.id}" = if not @post.approved and not @post.hidden_from_users and (can?(@conn, :hide, @post) or @post.user_id == @conn.assigns.current_user.id) do diff --git a/lib/philomena_web/templates/post/_post_options_top.html.slime b/lib/philomena_web/templates/post/_post_options_top.html.slime new file mode 100644 index 000000000..356b4f579 --- /dev/null +++ b/lib/philomena_web/templates/post/_post_options_top.html.slime @@ -0,0 +1,13 @@ +- post_link = ~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}?#{[post_id: @post.id]}" <> "#post_#{@post.id}" + +.flex__spacer.hidden--desktop +a.communication__interaction.hidden--desktop title="Link to post" href="#{post_link}" + i.fa.fa-link += if not is_nil(@post.edited_at) and can?(@conn, :show, @post) do + a href=~p"/forums/#{@post.topic.forum}/topics/#{@post.topic}/posts/#{@post}/history" + ' Edited + => pretty_time(@post.edited_at) + + = if @post.edit_reason not in [nil, ""] do + ' because: + => @post.edit_reason From f1b92ac25b9236cf276028270645973d69c9c091 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Sun, 23 Jun 2024 15:10:18 +0200 Subject: [PATCH 49/65] fix tests (botched typespec & commission listing shenanigans) --- lib/philomena/commissions/commission.ex | 22 ++++++++++++++++++- .../plugs/moderation_log_plug.ex | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/philomena/commissions/commission.ex b/lib/philomena/commissions/commission.ex index d8afead4b..2e800ccb0 100644 --- a/lib/philomena/commissions/commission.ex +++ b/lib/philomena/commissions/commission.ex @@ -42,7 +42,7 @@ defmodule Philomena.Commissions.Commission do |> validate_length(:contact, max: 1000, count: :bytes) |> validate_length(:will_create, max: 1000, count: :bytes) |> validate_length(:will_not_create, max: 1000, count: :bytes) - |> validate_subset(:categories, Keyword.values(categories())) + |> validate_subset(:categories, category_values()) end defp drop_blank_categories(changeset) do @@ -72,6 +72,26 @@ defmodule Philomena.Commissions.Commission do ] end + # This is probably not the best way to do this, + # but I need dialyzer to shut up and do so fast. + def category_values do + [ + "Anthro", + "Canon Characters", + "Comics", + "Fetish Art", + "Human and Human-like", + "NSFW", + "Original Characters", + "Original Species", + "Non-Humanoid", + "Requests", + "Safe", + "Shipping", + "Violence and Gore" + ] + end + def types do [ "Sketch", diff --git a/lib/philomena_web/plugs/moderation_log_plug.ex b/lib/philomena_web/plugs/moderation_log_plug.ex index c29f45a5a..e69da5708 100644 --- a/lib/philomena_web/plugs/moderation_log_plug.ex +++ b/lib/philomena_web/plugs/moderation_log_plug.ex @@ -17,7 +17,7 @@ defmodule PhilomenaWeb.ModerationLogPlug do def init(opts), do: opts @type log_details :: %{subject_path: String.t(), body: String.t()} - @type details_func :: (Plug.Conn.t(), atom(), any() -> log_details()) + @type details_func :: (atom(), any() -> log_details()) @type call_opts :: [details: details_func, data: any()] @doc false From 6e1cb36eb671b4afe1a666a6be9968f9f0fc233a Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Sun, 23 Jun 2024 19:21:24 +0200 Subject: [PATCH 50/65] convert notifications to typescript --- .../js/{notifications.js => notifications.ts} | 43 ++++++++----------- assets/js/utils/events.ts | 5 ++- 2 files changed, 23 insertions(+), 25 deletions(-) rename assets/js/{notifications.js => notifications.ts} (54%) diff --git a/assets/js/notifications.js b/assets/js/notifications.ts similarity index 54% rename from assets/js/notifications.js rename to assets/js/notifications.ts index 021678580..6ae7ee4ac 100644 --- a/assets/js/notifications.js +++ b/assets/js/notifications.ts @@ -5,20 +5,17 @@ import { fetchJson, handleError } from './utils/requests'; import { $ } from './utils/dom'; import { delegate } from './utils/events'; -import { assertNotNull } from './utils/assert'; +import { assertNotNull, assertNotUndefined } from './utils/assert'; import store from './utils/store'; const NOTIFICATION_INTERVAL = 600000, NOTIFICATION_EXPIRES = 300000; -function makeRequest(verb) { - return fetchJson(verb, '/notifications/unread').then(handleError); -} - function bindSubscriptionLinks() { delegate(document, 'fetchcomplete', { '.js-subscription-link': event => { - const target = event.target.closest('.js-subscription-target'); + const target = assertNotNull(event.target.closest('.js-subscription-target')); + event.detail.text().then(text => { target.outerHTML = text; }); @@ -31,40 +28,40 @@ function getNewNotifications() { return; } - makeRequest('GET').then(response => response.json()).then(({ notifications }) => { - updateNotificationTicker(notifications); - storeNotificationCount(notifications); + fetchJson('GET', '/notifications/unread') + .then(handleError).then(response => response.json()) + .then(({ notifications }) => { + updateNotificationTicker(notifications); + storeNotificationCount(notifications); - setTimeout(getNewNotifications, NOTIFICATION_INTERVAL); - }); + setTimeout(getNewNotifications, NOTIFICATION_INTERVAL); + }); } -function updateNotificationTicker(notificationCount) { - const ticker = assertNotNull($('.js-notification-ticker')); - const parsedNotificationCount = Number(notificationCount); +function updateNotificationTicker(notificationCount: unknown) { + const ticker = assertNotNull($('.js-notification-ticker')); + const parsedNotificationCount = Number(notificationCount as string); - ticker.dataset.notificationCount = parsedNotificationCount; - ticker.textContent = parsedNotificationCount; + ticker.dataset.notificationCount = parsedNotificationCount.toString(); + ticker.textContent = parsedNotificationCount.toString(); } - -function storeNotificationCount(notificationCount) { +function storeNotificationCount(notificationCount: string) { // The current number of notifications are stored along with the time when the data expires store.setWithExpireTime('notificationCount', notificationCount, NOTIFICATION_EXPIRES); } - -function setupNotifications() { +export function setupNotifications() { if (!window.booru.userIsSignedIn) return; // Fetch notifications from the server at a regular interval setTimeout(getNewNotifications, NOTIFICATION_INTERVAL); // Update the current number of notifications based on the latest page load - const ticker = $('.js-notification-ticker'); + const ticker = $('.js-notification-ticker'); if (ticker) { - storeNotificationCount(ticker.dataset.notificationCount); + storeNotificationCount(assertNotUndefined(ticker.dataset.notificationCount)); } // Update ticker when the stored value changes - this will occur in all open tabs @@ -72,5 +69,3 @@ function setupNotifications() { bindSubscriptionLinks(); } - -export { setupNotifications }; diff --git a/assets/js/utils/events.ts b/assets/js/utils/events.ts index 65c2c4960..e92b0109a 100644 --- a/assets/js/utils/events.ts +++ b/assets/js/utils/events.ts @@ -1,5 +1,7 @@ // DOM events +import '../../types/ujs'; + export interface PhilomenaAvailableEventsMap { dragstart: DragEvent, dragover: DragEvent, @@ -9,7 +11,8 @@ export interface PhilomenaAvailableEventsMap { drop: DragEvent, click: MouseEvent, submit: Event, - reset: Event + reset: Event, + fetchcomplete: FetchcompleteEvent } export interface PhilomenaEventElement { From b329f6d5aa53a6abb23e75a2426ecdcde5637d29 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Sun, 23 Jun 2024 19:59:06 +0200 Subject: [PATCH 51/65] convert misc scripts to ts --- assets/js/{misc.js => misc.ts} | 45 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 19 deletions(-) rename assets/js/{misc.js => misc.ts} (60%) diff --git a/assets/js/misc.js b/assets/js/misc.ts similarity index 60% rename from assets/js/misc.js rename to assets/js/misc.ts index c1a62b8d4..95f50d920 100644 --- a/assets/js/misc.js +++ b/assets/js/misc.ts @@ -4,35 +4,40 @@ import store from './utils/store'; import { $, $$ } from './utils/dom'; +import { assertNotNull } from './utils/assert'; +import '../types/ujs'; let touchMoved = false; -function formResult({target, detail}) { - - const elements = { +function formResult({target, detail}: FetchcompleteEvent) { + const elements: {[key: string]: string} = { '#description-form': '.image-description', '#uploader-form': '.image-uploader' }; - function showResult(resultEl, formEl, response) { + function showResult(resultEl: HTMLElement, formEl: HTMLFormElement, response: string) { resultEl.innerHTML = response; resultEl.classList.remove('hidden'); formEl.classList.add('hidden'); - formEl.querySelector('input[type="submit"],button').disabled = false; + const inputEl = $('input[type="submit"]', formEl); + const buttonEl = $('button', formEl); + + if (inputEl) inputEl.disabled = false; + if (buttonEl) buttonEl.disabled = false; } for (const element in elements) { - if (target.matches(element)) detail.text().then(text => showResult($(elements[element]), target, text)); + if (target.matches(element)) { + detail.text().then(text => showResult(assertNotNull($(elements[element])), target as HTMLFormElement, text)); + } } - } -function revealSpoiler(event) { - - const { target } = event; +function revealSpoiler(event: MouseEvent | TouchEvent) { + const target = assertNotNull(event.target) as HTMLElement; const spoiler = target.closest('.spoiler'); - let imgspoiler = target.closest('.spoiler .imgspoiler, .spoiler-revealed .imgspoiler'); const showContainer = target.closest('.image-show-container'); + let imgspoiler = target.closest('.spoiler .imgspoiler, .spoiler-revealed .imgspoiler'); // Prevent reveal if touchend came after touchmove event if (touchMoved) { @@ -42,7 +47,8 @@ function revealSpoiler(event) { if (spoiler) { if (showContainer) { - const imageShow = showContainer.querySelector('.image-show'); + const imageShow = assertNotNull(showContainer.querySelector('.image-show')); + if (!imageShow.classList.contains('hidden') && imageShow.classList.contains('spoiler-pending')) { imageShow.classList.remove('spoiler-pending'); return; @@ -62,19 +68,22 @@ function revealSpoiler(event) { if (imgspoiler) { imgspoiler.classList.remove('imgspoiler'); imgspoiler.classList.add('imgspoiler-revealed'); + if (event.type === 'touchend' && !event.defaultPrevented) { event.preventDefault(); } } - } -function setupEvents() { - const extrameta = $('#extrameta'); +export function setupEvents() { + const extrameta = $('#extrameta'); + + if (extrameta && store.get('hide_uploader')) { + extrameta.classList.add('hidden'); + } - if (store.get('hide_uploader') && extrameta) extrameta.classList.add('hidden'); if (store.get('hide_score')) { - $$('.upvotes,.score,.downvotes').forEach(s => s.classList.add('hidden')); + $$('.upvotes,.score,.downvotes').forEach(s => s.classList.add('hidden')); } document.addEventListener('fetchcomplete', formResult); @@ -82,5 +91,3 @@ function setupEvents() { document.addEventListener('touchend', revealSpoiler); document.addEventListener('touchmove', () => touchMoved = true); } - -export { setupEvents }; From bb45fcc651b872955c6b561309dfe07e5fa2a289 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Tue, 2 Jul 2024 20:09:30 +0200 Subject: [PATCH 52/65] filters and communications --- assets/css/common/mixins.css | 20 +++++ assets/css/elements/button.css | 4 +- assets/css/elements/list.css | 17 +++- assets/css/elements/media.css | 1 + assets/css/elements/table.css | 56 ++++++++++--- assets/css/views/markdown.css | 40 ++++++++++ .../templates/filter/_filter.html.slime | 78 ++++++++++++------- .../templates/filter/index.html.slime | 7 +- .../user_attribution/_user.html.slime | 2 +- lib/philomena_web/views/app_view.ex | 12 ++- 10 files changed, 188 insertions(+), 49 deletions(-) diff --git a/assets/css/common/mixins.css b/assets/css/common/mixins.css index ec72599ca..311ab539e 100644 --- a/assets/css/common/mixins.css +++ b/assets/css/common/mixins.css @@ -64,3 +64,23 @@ color var(--transition-animation-duration) ease, background var(--transition-animation-duration) ease; } + +@define-mixin no-bottom-border-radius { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +@define-mixin no-top-border-radius { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +@define-mixin first-last-child-border-radius $classname { + .$(classname) > *:first-child { + @mixin no-top-border-radius; + } + + $(classname) > *:last-child { + @mixin no-bottom-border-radius; + } +} diff --git a/assets/css/elements/button.css b/assets/css/elements/button.css index 97df77e75..18cbad765 100644 --- a/assets/css/elements/button.css +++ b/assets/css/elements/button.css @@ -103,7 +103,7 @@ width: fit-content; font-weight: bold; font-size: var(--font-size); - background: var(--primary-muted-color); + background: var(--secondary-dark-color); color: var(--link-color); border-radius: var(--border-radius-inner); padding: 0 var(--padding-small); @@ -117,7 +117,7 @@ .button:active { @mixin animated-transition; color: var(--text-color); - background: var(--primary-dark-color); + background: var(--secondary-muted-color); border-radius: var(--border-radius-inner); cursor: pointer; } diff --git a/assets/css/elements/list.css b/assets/css/elements/list.css index 574af0b32..ee4f4c824 100644 --- a/assets/css/elements/list.css +++ b/assets/css/elements/list.css @@ -1,10 +1,23 @@ ul, ol { - padding: 0; - list-style-type: none; + display: flex; + flex-flow: column; + gap: var(--padding-small); + margin: 0; + margin-left: var(--padding-small); + padding-inline-start: var(--padding-normal); +} + +li > ul, +li > ol { + padding-top: var(--padding-small); } .horizontal-list { display: flex; + flex-flow: row; gap: var(--padding-normal); + list-style-type: none; + padding: 0; + margin: 0; } diff --git a/assets/css/elements/media.css b/assets/css/elements/media.css index 748e38e31..b27d7a0e8 100644 --- a/assets/css/elements/media.css +++ b/assets/css/elements/media.css @@ -110,6 +110,7 @@ .image-container { display: flex; + position: relative; flex-direction: column; align-items: center; justify-content: center; diff --git a/assets/css/elements/table.css b/assets/css/elements/table.css index cdf9f7136..10efe5b08 100644 --- a/assets/css/elements/table.css +++ b/assets/css/elements/table.css @@ -1,27 +1,59 @@ -.table { +.table, +.communication__body__text table { border-collapse: collapse; width: 100%; } -.table > thead { - border-radius: var(--border-radius-inner); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; +.table thead, +.communication__body__text table thead { + background: var(--secondary-muted-color); } -.table > tbody { - border-radius: var(--border-radius-inner); - border-top-left-radius: 0; - border-top-right-radius: 0; +.table th { + background: var(--secondary-color); + padding: var(--padding-normal) var(--padding-small); } .table td, -th { +.table th { padding: var(--padding-small); - border: 1px solid var(--secondary-muted-color); text-align: left; } -.table th { +.communication__body__text table th { background: var(--secondary-color); + padding: var(--padding-normal) var(--padding-small); +} + +.communication__body__text table td, +.communication__body__text table th { + padding: var(--padding-small); + text-align: left; +} + +.table tr, +.communication__body__text table tr { + border-bottom: 1px solid var(--secondary-muted-color); +} + +.table thead th:last-child { + border-top-right-radius: var(--border-radius-inner); +} + +.table thead th:first-child { + border-top-left-radius: var(--border-radius-inner); +} + +.communication__body__text table thead th:first-child { + border-top-left-radius: var(--border-radius-inner); +} + +.communication__body__text table thead th:last-child { + border-top-right-radius: var(--border-radius-inner); +} + +.table tbody, +.communication__body__text table tbody { + border-top: 2px solid var(--secondary-muted-color); + border-bottom: 2px solid var(--secondary-muted-color); } diff --git a/assets/css/views/markdown.css b/assets/css/views/markdown.css index d3e3509f6..9efe06ce9 100644 --- a/assets/css/views/markdown.css +++ b/assets/css/views/markdown.css @@ -31,3 +31,43 @@ .walloftext { line-height: var(--readable-line-height); } + +pre, +code { + font-family: var(--font-family-monospace); + background: var(--background-color); + border: 1px solid var(--primary-border-color); + border-radius: var(--border-radius-inner); + padding: calc(var(--padding-tiny) / 2) var(--padding-tiny); +} + +pre { + padding: var(--padding-small); + line-height: var(--readable-line-height); + margin: var(--padding-small); + margin-right: 0; +} + +pre code { + border: 0; + background: 0; + padding: 0; +} + +blockquote { + position: relative; + margin: 0; + padding: var(--padding-normal); +} + +blockquote:before { + content: ""; + display: block; + position: absolute; + background: var(--secondary-color); + border-radius: var(--border-radius-inner); + width: var(--padding-tiny); + height: 100%; + left: 0; + top: 0; +} diff --git a/lib/philomena_web/templates/filter/_filter.html.slime b/lib/philomena_web/templates/filter/_filter.html.slime index ab4e92604..da9159d74 100644 --- a/lib/philomena_web/templates/filter/_filter.html.slime +++ b/lib/philomena_web/templates/filter/_filter.html.slime @@ -1,39 +1,59 @@ -.filter - h3 - = @filter.name +.block + .block__content + h3 = @filter.name - = if @filter.user do - p - ' Maintained by - = render PhilomenaWeb.UserAttributionView, "_user.html", object: @filter, conn: @conn - - = if @filter.system do - p - ' Maintained by staff - - .filter-options - ul - li - ' Spoilers - = length(@filter.spoilered_tag_ids) - ' , hides - = length(@filter.hidden_tag_ids) + .flex.flex--row + .button__group--standalone + = link to: ~p"/filters/#{@filter}" do + i.fa.fa-eye> + | View this filter + .separator--vertical + = link to: ~p"/filters/new?#{[based_on: @filter]}" do + i.fa.fa-copy> + | Copy and customize - li - = link "View this filter", to: ~p"/filters/#{@filter}", class: "button" - li - = link "Copy and Customize", to: ~p"/filters/new?#{[based_on: @filter]}", class: "button" = if can?(@conn, :edit, @filter) do - li - = link "Edit this filter", to: ~p"/filters/#{@filter}/edit", class: "button" + .button__group--standalone.button__group--warning + = link to: ~p"/filters/#{@filter}/edit" do + i.fa.fa-edit> + | Edit this filter = if @filter.id == @conn.assigns.current_filter.id do - li - strong Your current filter + .button__group--standalone.button__group--success + a + i.fa.fa-check> + | Your current filter - else - li - = button_to "Use this filter", ~p"/filters/current?#{[id: @filter]}", method: "put", class: "button" + .button__group--standalone + = button_to ~p"/filters/current?#{[id: @filter]}", method: "put", class: "button" do + i.fa.fa-arrow-right-to-bracket> + | Use this filter + + = cond do + - @filter.user -> + p + i.fa.fa-wrench.icon--fixed> + ' Maintained by + = render PhilomenaWeb.UserAttributionView, "_user.html", object: @filter, conn: @conn + - @filter.system -> + p + i.fa.fa-wrench.icon--fixed> + ' Maintained by site staff + - true -> + p + ' Unknown maintainer + p + i.fa.fa-shield.icon--fixed> + ' Spoilers + => length(@filter.spoilered_tag_ids) + ' tag(s) + p + i.fa.fa-eye-slash.icon--fixed> + ' Hides + => length(@filter.hidden_tag_ids) + ' tag(s) p + i.fa.fa-circle-info.icon--fixed> em = @filter.description diff --git a/lib/philomena_web/templates/filter/index.html.slime b/lib/philomena_web/templates/filter/index.html.slime index 8b94a091d..cebe5a471 100644 --- a/lib/philomena_web/templates/filter/index.html.slime +++ b/lib/philomena_web/templates/filter/index.html.slime @@ -27,12 +27,15 @@ = if !@conn.params["fq"] do h2 My Filters = if @current_user do - p - = link("Click here to make a new filter from scratch", to: ~p"/filters/new") + = link to: ~p"/filters/new", class: "button" do + i.fa.fa-plus> + | Click here to make a new filter from scratch + br = for filter <- @my_filters do = render PhilomenaWeb.FilterView, "_filter.html", conn: @conn, filter: filter - else p + i.fa.fa-circle-info.icon--fixed> ' If you're logged in, you can create and maintain custom filters here. h2 Global Filters diff --git a/lib/philomena_web/templates/user_attribution/_user.html.slime b/lib/philomena_web/templates/user_attribution/_user.html.slime index 848706b41..e2e88d78e 100644 --- a/lib/philomena_web/templates/user_attribution/_user.html.slime +++ b/lib/philomena_web/templates/user_attribution/_user.html.slime @@ -2,7 +2,7 @@ strong<> - icon = user_icon(@object.user) = if icon do - i class="fa #{icon}"> + i> class="fa #{icon}" = link(@object.user.name, to: ~p"/profiles/#{@object.user}") = if assigns[:awards] do = render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards diff --git a/lib/philomena_web/views/app_view.ex b/lib/philomena_web/views/app_view.ex index d6f3a7d88..a89e7130f 100644 --- a/lib/philomena_web/views/app_view.ex +++ b/lib/philomena_web/views/app_view.ex @@ -102,7 +102,17 @@ defmodule PhilomenaWeb.AppView do end end - def button_to(text, route, args \\ []) do + def button_to(route, args \\ [], do: fun) do + method = Keyword.get(args, :method, "get") + class = Keyword.get(args, :class, nil) + data = Keyword.get(args, :data, []) + + form_for(nil, route, [method: method, class: "button_to"], fn _f -> + submit([class: class, data: data], do: fun) + end) + end + + def button_to(text, route, args) do method = Keyword.get(args, :method, "get") class = Keyword.get(args, :class, nil) data = Keyword.get(args, :data, []) From c9846820ff4c4c8633becdf6f46a3fbe0e368f38 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Tue, 2 Jul 2024 21:32:39 +0200 Subject: [PATCH 53/65] simplify themes --- assets/css/themes/base/dark.css | 84 +++++++++++++++++++++++++++ assets/css/themes/base/light.css | 83 ++++++++++++++++++++++++++ assets/css/themes/dark-blue.css | 80 +------------------------ assets/css/themes/dark-green.css | 31 ++++++++++ assets/css/themes/dark-purple.css | 80 +------------------------ assets/css/themes/light-blue.css | 93 ++---------------------------- assets/css/themes/light-green.css | 32 ++++++++++ assets/css/themes/light-purple.css | 28 +++++++++ 8 files changed, 266 insertions(+), 245 deletions(-) create mode 100644 assets/css/themes/base/dark.css create mode 100644 assets/css/themes/base/light.css create mode 100644 assets/css/themes/dark-green.css create mode 100644 assets/css/themes/light-green.css create mode 100644 assets/css/themes/light-purple.css diff --git a/assets/css/themes/base/dark.css b/assets/css/themes/base/dark.css new file mode 100644 index 000000000..1c0532f17 --- /dev/null +++ b/assets/css/themes/base/dark.css @@ -0,0 +1,84 @@ +$text-color: #e0e0e0; + +$danger-color: #6d2a20; +$warning-color: #6d421a; +$success-color: #25603e; +$information-color: #1c606a; +$special-color: #65206e; + +$upvote-color: #5b9b26; +$downvote-color: #da3412; +$fave-color: #a18e27; +$comment-color: #b099dd; +$hide-color: #da3412; + +$tag-default-color: #1b3c21; +$tag-error-color: #4f181d; +$tag-rating-color: #113456; +$tag-origin-color: #1d1858; +$tag-character-color: #193f47; +$tag-oc-color: #451f47; +$tag-species-color: #362118; +$tag-body-type-color: #393939; +$tag-content-fanmade-color: #622c4e; +$tag-content-official-color: #4b491c; +$tag-spoiler-color: #4f3811; + +$spoiler-color: #0f0f0f; + +@define-mixin tag-color $tagname, $color, $text-percentage: 35, $border-percentage: 15 { + --tag-$(tagname)-color: $(color); + --tag-$(tagname)-border-color: hsl(from $color h s calc(l + $border-percentage)); + --tag-$(tagname)-text-color: hsl(from $color h s calc(l + $text-percentage)); +} + +@define-mixin type-color $type, $color { + --$(type)-color: $color; + --$(type)-border-color: hsl(from $color h calc(s - 20) calc(l + 10)); + --$(type)-muted-color: hsl(from $color h calc(s - 10) calc(l - 7)); + --$(type)-dark-color: hsl(from $color h calc(s - 30) calc(l - 11)); + --$(type)-link-color: hsl(from $color h calc(s + 10) calc(l + 45)); +} + +:root { + /* Main colors */ + --text-color: $text-color; + --text-light-color: $text-color; + + /* Link colors */ + --link-hover-color: $text-color; + + /* Interaction (vote) colors */ + --upvote-color: $upvote-color; + --downvote-color: $downvote-color; + --fave-color: $fave-color; + --comment-color: $comment-color; + --hide-color: $hide-color; + + /* Spoiler colors */ + --spoiler-color: $spoiler-color; + --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); + + /* The invisible color */ + --invisible-color: #00000000; + + /* Type (information category) colors */ + @mixin type-color success, $success-color; + @mixin type-color warning, $warning-color; + @mixin type-color danger, $danger-color; + @mixin type-color information, $information-color; + @mixin type-color special, $special-color; + + /* Tag colors */ + @mixin tag-color default, $tag-default-color; + @mixin tag-color error, $tag-error-color, 37; + @mixin tag-color rating, $tag-rating-color, 37; + @mixin tag-color origin, $tag-origin-color, 42; + @mixin tag-color character, $tag-character-color; + @mixin tag-color oc, $tag-oc-color, 40; + @mixin tag-color species, $tag-species-color, 37; + @mixin tag-color body-type, $tag-body-type-color, 45, 12; + @mixin tag-color content-fanmade, $tag-content-fanmade-color, 40; + @mixin tag-color content-official, $tag-content-official-color; + @mixin tag-color spoiler, $tag-spoiler-color; +} diff --git a/assets/css/themes/base/light.css b/assets/css/themes/base/light.css new file mode 100644 index 000000000..04c17fc9f --- /dev/null +++ b/assets/css/themes/base/light.css @@ -0,0 +1,83 @@ +$text-color: #121212; + +$danger-color: #e78f82; +$warning-color: #edb76c; +$success-color: #8de3b1; +$information-color: #90dce7; +$special-color: #dc77e9; + +$upvote-color: #67af2b; +$downvote-color: #cf0001; +$fave-color: #c4b246; +$comment-color: #9273d0; +$hide-color: #cf0001; + +$tag-default-color: #84dd96; +$tag-error-color: #eb848c; +$tag-rating-color: #9cc6f0; +$tag-origin-color: #b5b0ed; +$tag-character-color: #b6e4ed; +$tag-oc-color: #efaaf2; +$tag-species-color: #e1997a; +$tag-body-type-color: #bcbcbc; +$tag-content-fanmade-color: #e388c2; +$tag-content-official-color: #ebe9b3; +$tag-spoiler-color: #dcb879; + +$spoiler-color: #0f0f0f; + +@define-mixin tag-color $tagname, $color, $text-percentage: 40, $border-percentage: 15 { + --tag-$(tagname)-color: $(color); + --tag-$(tagname)-border-color: hsl(from $color h s calc(l - $border-percentage)); + --tag-$(tagname)-text-color: hsl(from $color h s calc(l - $text-percentage)); +} + +@define-mixin type-color $type, $color { + --$(type)-color: $color; + --$(type)-border-color: hsl(from $color h calc(s - 20) calc(l + 5)); + --$(type)-muted-color: hsl(from $color h calc(s - 7) calc(l - 12)); + --$(type)-dark-color: hsl(from $color h calc(s - 25) calc(l + 20)); + --$(type)-link-color: hsl(from $color h calc(s + 50) calc(l - 47)); +} + +:root { + /* Main colors */ + --text-color: $text-color; + + /* Link colors */ + --link-hover-color: $text-color; + + /* Interaction (vote) colors */ + --upvote-color: $upvote-color; + --downvote-color: $downvote-color; + --fave-color: $fave-color; + --comment-color: $comment-color; + --hide-color: $hide-color; + + /* Spoiler colors */ + --spoiler-color: $spoiler-color; + --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); + + /* The invisible color */ + --invisible-color: #00000000; + + /* Type (information category) colors */ + @mixin type-color success, $success-color; + @mixin type-color warning, $warning-color; + @mixin type-color danger, $danger-color; + @mixin type-color information, $information-color; + @mixin type-color special, $special-color; + + /* Tag colors */ + @mixin tag-color default, $tag-default-color; + @mixin tag-color error, $tag-error-color, 37; + @mixin tag-color rating, $tag-rating-color, 37; + @mixin tag-color origin, $tag-origin-color, 42; + @mixin tag-color character, $tag-character-color; + @mixin tag-color oc, $tag-oc-color, 40; + @mixin tag-color species, $tag-species-color, 37; + @mixin tag-color body-type, $tag-body-type-color, 45, 12; + @mixin tag-color content-fanmade, $tag-content-fanmade-color, 40; + @mixin tag-color content-official, $tag-content-official-color; + @mixin tag-color spoiler, $tag-spoiler-color; +} diff --git a/assets/css/themes/dark-blue.css b/assets/css/themes/dark-blue.css index f96527689..861fddd1b 100644 --- a/assets/css/themes/dark-blue.css +++ b/assets/css/themes/dark-blue.css @@ -1,57 +1,15 @@ -$background-color: #0f161e; -$text-color: #e0e0e0; +@import "themes/base/dark"; +$background-color: #0f161e; $primary-color: #284371; $secondary-color: #546c99; -$danger-color: #6d2a20; -$warning-color: #6d421a; -$success-color: #25603e; -$information-color: #1c606a; -$special-color: #65206e; - -$upvote-color: #5b9b26; -$downvote-color: #da3412; -$fave-color: #a18e27; -$comment-color: #b099dd; -$hide-color: #da3412; - -$tag-default-color: #1b3c21; -$tag-error-color: #4f181d; -$tag-rating-color: #113456; -$tag-origin-color: #1d1858; -$tag-character-color: #193f47; -$tag-oc-color: #451f47; -$tag-species-color: #362118; -$tag-body-type-color: #393939; -$tag-content-fanmade-color: #622c4e; -$tag-content-official-color: #4b491c; -$tag-spoiler-color: #4f3811; - -$spoiler-color: #0f0f0f; - -@define-mixin tag-color $tagname, $color, $text-percentage: 35, $border-percentage: 15 { - --tag-$(tagname)-color: $(color); - --tag-$(tagname)-border-color: hsl(from $color h s calc(l + $border-percentage)); - --tag-$(tagname)-text-color: hsl(from $color h s calc(l + $text-percentage)); -} - -@define-mixin type-color $type, $color { - --$(type)-color: $color; - --$(type)-border-color: hsl(from $color h calc(s - 20) calc(l + 10)); - --$(type)-muted-color: hsl(from $color h calc(s - 10) calc(l - 7)); - --$(type)-dark-color: hsl(from $color h calc(s - 30) calc(l - 11)); - --$(type)-link-color: hsl(from $color h calc(s + 10) calc(l + 45)); -} :root { /* Main colors */ --background-color: $background-color; - --text-color: $text-color; - --text-light-color: $text-color; /* Link colors */ --link-color: hsl(from $primary-color h calc(s + 10) calc(l + 40)); - --link-hover-color: $text-color; /* Primary color (block backgrounds and such) */ --primary-color: $primary-color; @@ -66,38 +24,4 @@ $spoiler-color: #0f0f0f; --secondary-muted-color: hsl(from $secondary-color h s calc(l - 17)); --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l - 25)); --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); - - /* Interaction (vote) colors */ - --upvote-color: $upvote-color; - --downvote-color: $downvote-color; - --fave-color: $fave-color; - --comment-color: $comment-color; - --hide-color: $hide-color; - - /* Spoiler colors */ - --spoiler-color: $spoiler-color; - --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); - - /* The invisible color */ - --invisible-color: #00000000; - - /* Type (information category) colors */ - @mixin type-color success, $success-color; - @mixin type-color warning, $warning-color; - @mixin type-color danger, $danger-color; - @mixin type-color information, $information-color; - @mixin type-color special, $special-color; - - /* Tag colors */ - @mixin tag-color default, $tag-default-color; - @mixin tag-color error, $tag-error-color, 37; - @mixin tag-color rating, $tag-rating-color, 37; - @mixin tag-color origin, $tag-origin-color, 42; - @mixin tag-color character, $tag-character-color; - @mixin tag-color oc, $tag-oc-color, 40; - @mixin tag-color species, $tag-species-color, 37; - @mixin tag-color body-type, $tag-body-type-color, 45, 12; - @mixin tag-color content-fanmade, $tag-content-fanmade-color, 40; - @mixin tag-color content-official, $tag-content-official-color; - @mixin tag-color spoiler, $tag-spoiler-color; } diff --git a/assets/css/themes/dark-green.css b/assets/css/themes/dark-green.css new file mode 100644 index 000000000..bd7791a0c --- /dev/null +++ b/assets/css/themes/dark-green.css @@ -0,0 +1,31 @@ +@import "themes/base/dark"; + +$background-color: #131513; +$primary-color: #235c36; +$secondary-color: #3f7a64; +$success-color: #1c7843; + +:root { + /* Main colors */ + --background-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s - 10) calc(l + 40)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s - 20) calc(l + 5)); + --primary-muted-color: hsl(from $primary-color h calc(s - 10) calc(l - 10)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 15)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l - 17)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l - 23)); + --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); + + /* Type (information category) colors */ + @mixin type-color success, $success-color; +} diff --git a/assets/css/themes/dark-purple.css b/assets/css/themes/dark-purple.css index fca9dde73..a74afacfc 100644 --- a/assets/css/themes/dark-purple.css +++ b/assets/css/themes/dark-purple.css @@ -1,57 +1,15 @@ -$background-color: #15121a; -$text-color: #e0e0e0; +@import "themes/base/dark"; +$background-color: #15121a; $primary-color: #36274e; $secondary-color: #785b99; -$danger-color: #6d2a20; -$warning-color: #715227; -$success-color: #25603e; -$information-color: #1c606a; -$special-color: #65206e; - -$upvote-color: #5b9b26; -$downvote-color: #da3412; -$fave-color: #a18e27; -$comment-color: #b099dd; -$hide-color: #da3412; - -$tag-default-color: #1b3c21; -$tag-error-color: #4f181d; -$tag-rating-color: #113456; -$tag-origin-color: #1d1858; -$tag-character-color: #193f47; -$tag-oc-color: #451f47; -$tag-species-color: #362118; -$tag-body-type-color: #393939; -$tag-content-fanmade-color: #622c4e; -$tag-content-official-color: #4b491c; -$tag-spoiler-color: #4f3811; - -$spoiler-color: #0f0f0f; - -@define-mixin tag-color $tagname, $color, $text-percentage: 35, $border-percentage: 15 { - --tag-$(tagname)-color: $(color); - --tag-$(tagname)-border-color: hsl(from $color h s calc(l + $border-percentage)); - --tag-$(tagname)-text-color: hsl(from $color h s calc(l + $text-percentage)); -} - -@define-mixin type-color $type, $color { - --$(type)-color: $color; - --$(type)-border-color: hsl(from $color h calc(s - 20) calc(l + 10)); - --$(type)-muted-color: hsl(from $color h calc(s - 10) calc(l - 7)); - --$(type)-dark-color: hsl(from $color h calc(s - 30) calc(l - 11)); - --$(type)-link-color: hsl(from $color h calc(s + 10) calc(l + 45)); -} :root { /* Main colors */ --background-color: $background-color; - --text-color: $text-color; - --text-light-color: $text-color; /* Link colors */ --link-color: hsl(from $primary-color h calc(s + 10) calc(l + 50)); - --link-hover-color: $text-color; /* Primary color (block backgrounds and such) */ --primary-color: $primary-color; @@ -66,38 +24,4 @@ $spoiler-color: #0f0f0f; --secondary-muted-color: hsl(from $secondary-color h s calc(l - 17)); --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l - 25)); --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); - - /* Interaction (vote) colors */ - --upvote-color: $upvote-color; - --downvote-color: $downvote-color; - --fave-color: $fave-color; - --comment-color: $comment-color; - --hide-color: $hide-color; - - /* Spoiler colors */ - --spoiler-color: $spoiler-color; - --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); - - /* The invisible color */ - --invisible-color: #00000000; - - /* Type (information category) colors */ - @mixin type-color success, $success-color; - @mixin type-color warning, $warning-color; - @mixin type-color danger, $danger-color; - @mixin type-color information, $information-color; - @mixin type-color special, $special-color; - - /* Tag colors */ - @mixin tag-color default, $tag-default-color; - @mixin tag-color error, $tag-error-color, 37; - @mixin tag-color rating, $tag-rating-color, 37; - @mixin tag-color origin, $tag-origin-color, 42; - @mixin tag-color character, $tag-character-color; - @mixin tag-color oc, $tag-oc-color, 40; - @mixin tag-color species, $tag-species-color, 37; - @mixin tag-color body-type, $tag-body-type-color, 45, 12; - @mixin tag-color content-fanmade, $tag-content-fanmade-color, 40; - @mixin tag-color content-official, $tag-content-official-color; - @mixin tag-color spoiler, $tag-spoiler-color; } diff --git a/assets/css/themes/light-blue.css b/assets/css/themes/light-blue.css index aaa031d5d..57596324c 100644 --- a/assets/css/themes/light-blue.css +++ b/assets/css/themes/light-blue.css @@ -1,57 +1,16 @@ -$background-color: #f7f7f7; -$text-color: #121212; +@import "themes/base/light"; +$background-color: #f7f7f7; $primary-color: #4b9ad3; $secondary-color: #62a7d9; -$danger-color: #e78f82; -$warning-color: #edb76c; -$success-color: #8de3b1; -$information-color: #90dce7; -$special-color: #dc77e9; - -$upvote-color: #67af2b; -$downvote-color: #cf0001; -$fave-color: #c4b246; -$comment-color: #9273d0; -$hide-color: #cf0001; - -$tag-default-color: #84dd96; -$tag-error-color: #eb848c; -$tag-rating-color: #9cc6f0; -$tag-origin-color: #b5b0ed; -$tag-character-color: #b6e4ed; -$tag-oc-color: #efaaf2; -$tag-species-color: #e1997a; -$tag-body-type-color: #bcbcbc; -$tag-content-fanmade-color: #e388c2; -$tag-content-official-color: #ebe9b3; -$tag-spoiler-color: #dcb879; - -$spoiler-color: #0f0f0f; - -@define-mixin tag-color $tagname, $color, $text-percentage: 40, $border-percentage: 15 { - --tag-$(tagname)-color: $(color); - --tag-$(tagname)-border-color: hsl(from $color h s calc(l - $border-percentage)); - --tag-$(tagname)-text-color: hsl(from $color h s calc(l - $text-percentage)); -} - -@define-mixin type-color $type, $color { - --$(type)-color: $color; - --$(type)-border-color: hsl(from $color h calc(s - 20) calc(l + 5)); - --$(type)-muted-color: hsl(from $color h calc(s - 7) calc(l - 12)); - --$(type)-dark-color: hsl(from $color h calc(s - 25) calc(l + 20)); - --$(type)-link-color: hsl(from $color h calc(s + 50) calc(l - 47)); -} :root { /* Main colors */ --background-color: $background-color; - --text-color: $text-color; --text-light-color: $background-color; /* Link colors */ --link-color: hsl(from $primary-color h calc(s + 25) calc(l - 25)); - --link-hover-color: $text-color; /* Primary color (block backgrounds and such) */ --primary-color: $primary-color; @@ -66,58 +25,14 @@ $spoiler-color: #0f0f0f; --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 18)); --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l + 25)); --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); - - /* Interaction (vote) colors */ - --upvote-color: $upvote-color; - --downvote-color: $downvote-color; - --fave-color: $fave-color; - --comment-color: $comment-color; - --hide-color: $hide-color; - - /* Spoiler colors */ - --spoiler-color: $spoiler-color; - --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); - - /* The invisible color */ - --invisible-color: #00000000; - - /* Type (information category) colors */ - @mixin type-color success, $success-color; - @mixin type-color warning, $warning-color; - @mixin type-color danger, $danger-color; - @mixin type-color information, $information-color; - @mixin type-color special, $special-color; - - /* Tag colors */ - @mixin tag-color default, $tag-default-color; - @mixin tag-color error, $tag-error-color, 37; - @mixin tag-color rating, $tag-rating-color, 37; - @mixin tag-color origin, $tag-origin-color, 42; - @mixin tag-color character, $tag-character-color; - @mixin tag-color oc, $tag-oc-color, 40; - @mixin tag-color species, $tag-species-color, 37; - @mixin tag-color body-type, $tag-body-type-color, 45, 12; - @mixin tag-color content-fanmade, $tag-content-fanmade-color, 40; - @mixin tag-color content-official, $tag-content-official-color; - @mixin tag-color spoiler, $tag-spoiler-color; } -header { +nav.header__secondary { /* Apparently people will put me on a cross if I don't use Trixie colors, so here you go! */ background-color: #e2ebf2; } -/* Sigh... my poor poor pretty CSS... */ -.header__search { - background-color: var(--primary-color); -} - -.header__search * { - color: var(--text-light-color) !important; -} - -.input:focus, -.header__search > .input:hover { +nav.header__secondary .header__link { color: var(--text-color) !important; } diff --git a/assets/css/themes/light-green.css b/assets/css/themes/light-green.css new file mode 100644 index 000000000..bfe93365a --- /dev/null +++ b/assets/css/themes/light-green.css @@ -0,0 +1,32 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #43b268; +$secondary-color: #88c3ac; +$success-color: #6be49d; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h s calc(l - 12)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s + 5) calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h calc(s + 7) calc(l + 35)); + --primary-dark-color: hsl(from $primary-color h s calc(l + 45)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 18)); + --secondary-dark-color: hsl(from $secondary-color h calc(s + 5) calc(l + 23)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); + + /* Type (information category) colors */ + @mixin type-color success, $success-color; +} diff --git a/assets/css/themes/light-purple.css b/assets/css/themes/light-purple.css new file mode 100644 index 000000000..6d704e2df --- /dev/null +++ b/assets/css/themes/light-purple.css @@ -0,0 +1,28 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #9c72e0; +$secondary-color: #9f74cf; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h s calc(l - 7)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s + 5) calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h calc(s + 7) calc(l + 20)); + --primary-dark-color: hsl(from $primary-color h s calc(l + 28)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 18)); + --secondary-dark-color: hsl(from $secondary-color h calc(s + 5) calc(l + 23)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); +} From 038d74d17cb31aa6be972d273a836fa10a52012f Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Tue, 2 Jul 2024 21:35:38 +0200 Subject: [PATCH 54/65] change header --- assets/css/common/measurements.css | 4 +- assets/css/elements/dropdown.css | 39 +++++++++++++++++++ assets/css/views/admin.css | 9 +++++ assets/css/views/header.css | 7 ++-- .../_header_navigation_staff.html.slime | 4 +- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/assets/css/common/measurements.css b/assets/css/common/measurements.css index 81a7119c2..d4cd5b03e 100644 --- a/assets/css/common/measurements.css +++ b/assets/css/common/measurements.css @@ -22,8 +22,8 @@ $font-family-heading: $font-family-base; --block-header-height: 2rem; /* Navbar */ - --navbar-size: 4rem; - --navbar-secondary-size: 2.25rem; + --navbar-size: 3rem; + --navbar-secondary-size: 2.5rem; --navbar-input-size: 2rem; --navbar-input-width: 18vw; --navbar-input-max-width: 22rem; diff --git a/assets/css/elements/dropdown.css b/assets/css/elements/dropdown.css index c7cb8f6af..53e556c6e 100644 --- a/assets/css/elements/dropdown.css +++ b/assets/css/elements/dropdown.css @@ -1,3 +1,35 @@ +@define-mixin dropdown-type $type { + .dropdown--$(type) .dropdown__content { + background: var(--$(type)-muted-color); + border-color: var(--$(type)-border-color); + } + + .dropdown--$(type) .dropdown__text { + background: var(--$(type)-muted-color); + border-color: var(--$(type)-border-color); + } + + .dropdown--$(type) .dropdown__item:hover .dropdown__text { + background: var(--$(type)-color); + } + + .dropdown--$(type) .dropdown__icon { + background: var(--$(type)-dark-color); + } + + .dropdown--$(type) .dropdown__item:hover .dropdown__icon { + background: var(--$(type)-color); + } + + .dropdown--$(type) .dropdown__separator { + border-color: var(--$(type)-border-color); + } + + .dropdown--$(type) .dropdown__link:hover { + background: var(--$(type)-color); + } +} + .dropdown { position: relative; display: inline-block; @@ -68,3 +100,10 @@ .dropdown__link:hover { background: var(--primary-color); } + +@mixin dropdown-type secondary; +@mixin dropdown-type success; +@mixin dropdown-type warning; +@mixin dropdown-type danger; +@mixin dropdown-type information; +@mixin dropdown-type special; diff --git a/assets/css/views/admin.css b/assets/css/views/admin.css index 25d182374..7cb54c658 100644 --- a/assets/css/views/admin.css +++ b/assets/css/views/admin.css @@ -2,6 +2,7 @@ nav.header__staff { display: flex; line-height: var(--navbar-secondary-size); border-radius: var(--border-radius-outer); + margin: auto; } .header__counter__admin { @@ -25,6 +26,14 @@ nav.header__staff { border-radius: var(--border-radius-outer); } +.header--secondary__admin-links .header__link { + color: var(--text-color) !important; +} + +.header--secondary__admin-links .header__link:hover { + color: var(--danger-link-color) !important; +} + @mixin if-mobile { .header--secondary__admin-links { justify-content: center; diff --git a/assets/css/views/header.css b/assets/css/views/header.css index 96dc8cc7f..87777c2c1 100644 --- a/assets/css/views/header.css +++ b/assets/css/views/header.css @@ -5,12 +5,13 @@ header { font-size: var(--font-header-size); gap: var(--padding-large); align-items: center; + background: var(--primary-color); } .header__link { @mixin animated-transition; position: relative; - color: var(--text-color) !important; + color: var(--text-light-color) !important; } .header__link:hover { @@ -58,6 +59,7 @@ header { } /* Old style counter */ +/* todo: remove */ .header__counter--simple { display: inline-flex; padding: 0 var(--padding-small); @@ -98,8 +100,8 @@ header { .nav-container { display: flex; + flex-flow: column; gap: var(--padding-normal); - padding: 0 var(--padding-normal); flex-wrap: wrap; } @@ -107,7 +109,6 @@ nav.header__secondary { display: flex; flex: 1 0 auto; line-height: var(--navbar-secondary-size); - border-radius: var(--border-radius-outer); padding: 0 var(--padding-large); font-size: var(--font-header-size); gap: var(--padding-large); diff --git a/lib/philomena_web/templates/layout/_header_navigation_staff.html.slime b/lib/philomena_web/templates/layout/_header_navigation_staff.html.slime index 7b9300425..bf5941f75 100644 --- a/lib/philomena_web/templates/layout/_header_navigation_staff.html.slime +++ b/lib/philomena_web/templates/layout/_header_navigation_staff.html.slime @@ -1,6 +1,6 @@ .header--secondary__admin-links.js-staff-action = if manages_users?(@conn) do - .dropdown.hidden--mobile + .dropdown.dropdown--danger.hidden--mobile a.header__link title="Admin" i.fa.fa-bars> i.fa.fa-caret-down @@ -108,7 +108,7 @@ = @dnp_entry_count = if manages_bans?(@conn) do - .dropdown.hidden--mobile + .dropdown.dropdown--danger.hidden--mobile a.header__link title="Bans" i.fa.fa-gavel> span From 7bb79bc17fdc1def57442399f6f05695b5cfa8dc Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Tue, 2 Jul 2024 21:42:21 +0200 Subject: [PATCH 55/65] fix warnings --- assets/css/elements/dropdown.css | 8 ++++---- lib/philomena_web/views/app_view.ex | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/assets/css/elements/dropdown.css b/assets/css/elements/dropdown.css index 53e556c6e..72d2b348e 100644 --- a/assets/css/elements/dropdown.css +++ b/assets/css/elements/dropdown.css @@ -12,19 +12,19 @@ .dropdown--$(type) .dropdown__item:hover .dropdown__text { background: var(--$(type)-color); } - + .dropdown--$(type) .dropdown__icon { background: var(--$(type)-dark-color); } - + .dropdown--$(type) .dropdown__item:hover .dropdown__icon { background: var(--$(type)-color); } - + .dropdown--$(type) .dropdown__separator { border-color: var(--$(type)-border-color); } - + .dropdown--$(type) .dropdown__link:hover { background: var(--$(type)-color); } diff --git a/lib/philomena_web/views/app_view.ex b/lib/philomena_web/views/app_view.ex index c64a1dac7..b6eb67921 100644 --- a/lib/philomena_web/views/app_view.ex +++ b/lib/philomena_web/views/app_view.ex @@ -102,7 +102,9 @@ defmodule PhilomenaWeb.AppView do end end - def button_to(route, args \\ [], do: fun) do + def button_to(text, route, args \\ []) + + def button_to(route, args, do: fun) do method = Keyword.get(args, :method, "get") class = Keyword.get(args, :class, nil) data = Keyword.get(args, :data, []) From 774072945aa48096b284cdee13c1146e3de692d9 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Tue, 2 Jul 2024 22:43:06 +0200 Subject: [PATCH 56/65] the remaining themes --- assets/css/themes/dark-cyan.css | 27 ++++++++++++++++++++++ assets/css/themes/dark-orange.css | 27 ++++++++++++++++++++++ assets/css/themes/dark-pink.css | 27 ++++++++++++++++++++++ assets/css/themes/dark-red.css | 27 ++++++++++++++++++++++ assets/css/themes/dark-silver.css | 27 ++++++++++++++++++++++ assets/css/themes/dark-yellow.css | 27 ++++++++++++++++++++++ assets/css/themes/light-cyan.css | 36 ++++++++++++++++++++++++++++++ assets/css/themes/light-orange.css | 28 +++++++++++++++++++++++ assets/css/themes/light-pink.css | 36 ++++++++++++++++++++++++++++++ assets/css/themes/light-red.css | 32 ++++++++++++++++++++++++++ assets/css/themes/light-silver.css | 36 ++++++++++++++++++++++++++++++ assets/css/themes/light-yellow.css | 36 ++++++++++++++++++++++++++++++ 12 files changed, 366 insertions(+) create mode 100644 assets/css/themes/dark-cyan.css create mode 100644 assets/css/themes/dark-orange.css create mode 100644 assets/css/themes/dark-pink.css create mode 100644 assets/css/themes/dark-red.css create mode 100644 assets/css/themes/dark-silver.css create mode 100644 assets/css/themes/dark-yellow.css create mode 100644 assets/css/themes/light-cyan.css create mode 100644 assets/css/themes/light-orange.css create mode 100644 assets/css/themes/light-pink.css create mode 100644 assets/css/themes/light-red.css create mode 100644 assets/css/themes/light-silver.css create mode 100644 assets/css/themes/light-yellow.css diff --git a/assets/css/themes/dark-cyan.css b/assets/css/themes/dark-cyan.css new file mode 100644 index 000000000..5346e28c7 --- /dev/null +++ b/assets/css/themes/dark-cyan.css @@ -0,0 +1,27 @@ +@import "themes/base/dark"; + +$background-color: #0c1519; +$primary-color: #28728f; +$secondary-color: #4a8486; + +:root { + /* Main colors */ + --background-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 10) calc(l + 40)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s - 20) calc(l + 5)); + --primary-muted-color: hsl(from $primary-color h calc(s - 10) calc(l - 15)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 25)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l - 17)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l - 20)); + --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); +} diff --git a/assets/css/themes/dark-orange.css b/assets/css/themes/dark-orange.css new file mode 100644 index 000000000..f8b6f419c --- /dev/null +++ b/assets/css/themes/dark-orange.css @@ -0,0 +1,27 @@ +@import "themes/base/dark"; + +$background-color: #1b1511; +$primary-color: #8c4010; +$secondary-color: #a1654d; + +:root { + /* Main colors */ + --background-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 15) calc(l + 40)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s - 20) calc(l + 5)); + --primary-muted-color: hsl(from $primary-color h calc(s - 5) calc(l - 14)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 19)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l - 15)); + --secondary-dark-color: hsl(from $secondary-color h calc(s + 10) calc(l - 28)); + --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); +} diff --git a/assets/css/themes/dark-pink.css b/assets/css/themes/dark-pink.css new file mode 100644 index 000000000..a5b11571b --- /dev/null +++ b/assets/css/themes/dark-pink.css @@ -0,0 +1,27 @@ +@import "themes/base/dark"; + +$background-color: #1a0f14; +$primary-color: #871647; +$secondary-color: #7e4953; + +:root { + /* Main colors */ + --background-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 20) calc(l + 40)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s - 20) calc(l + 5)); + --primary-muted-color: hsl(from $primary-color h calc(s - 10) calc(l - 12)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 19)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l - 17)); + --secondary-dark-color: hsl(from $secondary-color h calc(s + 5) calc(l - 20)); + --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); +} diff --git a/assets/css/themes/dark-red.css b/assets/css/themes/dark-red.css new file mode 100644 index 000000000..3f23a5454 --- /dev/null +++ b/assets/css/themes/dark-red.css @@ -0,0 +1,27 @@ +@import "themes/base/dark"; + +$background-color: #1a1212; +$primary-color: #842c2c; +$secondary-color: #99685b; + +:root { + /* Main colors */ + --background-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 10) calc(l + 50)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s - 20) calc(l + 5)); + --primary-muted-color: hsl(from $primary-color h calc(s - 10) calc(l - 11)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 18)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l - 17)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l - 25)); + --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); +} diff --git a/assets/css/themes/dark-silver.css b/assets/css/themes/dark-silver.css new file mode 100644 index 000000000..9a8eb54f8 --- /dev/null +++ b/assets/css/themes/dark-silver.css @@ -0,0 +1,27 @@ +@import "themes/base/dark"; + +$background-color: #131618; +$primary-color: #36454f; +$secondary-color: #444444; + +:root { + /* Main colors */ + --background-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s - 15) calc(l + 40)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s - 20) calc(l + 5)); + --primary-muted-color: hsl(from $primary-color h calc(s - 10) calc(l - 10)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 14)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l - 5)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l - 10)); + --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); +} diff --git a/assets/css/themes/dark-yellow.css b/assets/css/themes/dark-yellow.css new file mode 100644 index 000000000..c18ca7e20 --- /dev/null +++ b/assets/css/themes/dark-yellow.css @@ -0,0 +1,27 @@ +@import "themes/base/dark"; + +$background-color: #191810; +$primary-color: #7a7200; +$secondary-color: #7e6b36; + +:root { + /* Main colors */ + --background-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 10) calc(l + 40)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s - 20) calc(l + 5)); + --primary-muted-color: hsl(from $primary-color h calc(s - 10) calc(l - 10)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 16)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l - 10)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l - 15)); + --secondary-link-color: hsl(from $secondary-color h s calc(l + 40)); +} diff --git a/assets/css/themes/light-cyan.css b/assets/css/themes/light-cyan.css new file mode 100644 index 000000000..c394c04e5 --- /dev/null +++ b/assets/css/themes/light-cyan.css @@ -0,0 +1,36 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #5bcefa; +$secondary-color: #74c7c7; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 25) calc(l - 35)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s + 5) calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h calc(s + 7) calc(l + 22)); + --primary-dark-color: hsl(from $primary-color h calc(s + 20) calc(l + 29)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 18)); + --secondary-dark-color: hsl(from $secondary-color h calc(s + 5) calc(l + 22)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); +} + +.header__link { + color: var(--text-color) !important; +} + +nav.header__secondary .header__link { + color: var(--text-color) !important; +} diff --git a/assets/css/themes/light-orange.css b/assets/css/themes/light-orange.css new file mode 100644 index 000000000..8d5110e0a --- /dev/null +++ b/assets/css/themes/light-orange.css @@ -0,0 +1,28 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #f48906; +$secondary-color: #e2a344; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 10) calc(l - 25)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s + 5) calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h calc(s + 7) calc(l + 33)); + --primary-dark-color: hsl(from $primary-color h calc(s + 20) calc(l + 44)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 14)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l + 20)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); +} diff --git a/assets/css/themes/light-pink.css b/assets/css/themes/light-pink.css new file mode 100644 index 000000000..9f12233a6 --- /dev/null +++ b/assets/css/themes/light-pink.css @@ -0,0 +1,36 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #f5a9b8; +$secondary-color: #cd6994; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 20) calc(l - 35)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s + 5) calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h calc(s + 10) calc(l + 9)); + --primary-dark-color: hsl(from $primary-color h calc(s + 20) calc(l + 14)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 18)); + --secondary-dark-color: hsl(from $secondary-color h calc(s + 5) calc(l + 25)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); +} + +.header__link { + color: var(--text-color) !important; +} + +nav.header__secondary .header__link { + color: var(--text-color) !important; +} diff --git a/assets/css/themes/light-red.css b/assets/css/themes/light-red.css new file mode 100644 index 000000000..8247a30ab --- /dev/null +++ b/assets/css/themes/light-red.css @@ -0,0 +1,32 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #e35a5a; +$secondary-color: #ea7363; +$danger-color: #df5858; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 10) calc(l - 25)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s + 5) calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h calc(s + 7) calc(l + 22)); + --primary-dark-color: hsl(from $primary-color h calc(s + 20) calc(l + 32)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 14)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l + 20)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); + + /* Type (information category) colors */ + @mixin type-color danger, $danger-color; +} diff --git a/assets/css/themes/light-silver.css b/assets/css/themes/light-silver.css new file mode 100644 index 000000000..ebd3c8707 --- /dev/null +++ b/assets/css/themes/light-silver.css @@ -0,0 +1,36 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #c0c0c0; /* i'm a chicken :D */ +$secondary-color: #c7c7c7; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h s calc(l - 50)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h s calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h s calc(l + 10)); + --primary-dark-color: hsl(from $primary-color h s calc(l + 17)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h s calc(l + 2)); + --secondary-dark-color: hsl(from $secondary-color h s calc(l + 5)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); +} + +.header__link { + color: var(--text-color) !important; +} + +nav.header__secondary .header__link { + color: var(--text-color) !important; +} diff --git a/assets/css/themes/light-yellow.css b/assets/css/themes/light-yellow.css new file mode 100644 index 000000000..5867833d0 --- /dev/null +++ b/assets/css/themes/light-yellow.css @@ -0,0 +1,36 @@ +@import "themes/base/light"; + +$background-color: #f7f7f7; +$primary-color: #efe00a; +$secondary-color: #cac467; + +:root { + /* Main colors */ + --background-color: $background-color; + --text-light-color: $background-color; + + /* Link colors */ + --link-color: hsl(from $primary-color h calc(s + 45) calc(l - 32)); + + /* Primary color (block backgrounds and such) */ + --primary-color: $primary-color; + --primary-border-color: hsl(from $primary-color h calc(s + 5) calc(l - 5)); + --primary-muted-color: hsl(from $primary-color h calc(s + 7) calc(l + 27)); + --primary-dark-color: hsl(from $primary-color h calc(s + 10) calc(l + 46)); + --primary-link-color: var(--link-color); /* for consistency */ + + /* Secondary color (most buttons) */ + --secondary-color: $secondary-color; + --secondary-border-color: hsl(from $secondary-color h s calc(l - 45)); + --secondary-muted-color: hsl(from $secondary-color h calc(s + 5) calc(l + 18)); + --secondary-dark-color: hsl(from $secondary-color h calc(s - 5) calc(l + 22)); + --secondary-link-color: hsl(from $secondary-color h s calc(l - 55)); +} + +.header__link { + color: var(--text-color) !important; +} + +nav.header__secondary .header__link { + color: var(--text-color) !important; +} From 625c30aaae7a04f3dd50c1751dc88bb89586c523 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 3 Jul 2024 21:32:27 +0200 Subject: [PATCH 57/65] functioning theme selector --- assets/js/settings.ts | 19 ++-- lib/philomena/users/user.ex | 2 +- .../controllers/setting_controller.ex | 19 +++- .../templates/layout/app.html.slime | 4 +- .../templates/setting/edit.html.slime | 18 +-- lib/philomena_web/views/layout_view.ex | 18 +-- lib/philomena_web/views/setting_view.ex | 105 +++--------------- 7 files changed, 58 insertions(+), 127 deletions(-) diff --git a/assets/js/settings.ts b/assets/js/settings.ts index 9ff6b17fd..e4ab89278 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -11,8 +11,9 @@ export function setupSettings() { if (!$('#js-setting-table')) return; const localCheckboxes = $$('[data-tab="local"] input[type="checkbox"]'); - const themeSelect = assertNotNull($('#user_theme')); - const styleSheet = assertNotNull($('head link[rel="stylesheet"]')); + const themeSelect = assertNotNull($('#user_theme_name')); + const themeColorSelect = assertNotNull($('#user_theme_color')); + const styleSheet = assertNotNull($('#js-theme-stylesheet')); // Local settings localCheckboxes.forEach(checkbox => { @@ -22,9 +23,13 @@ export function setupSettings() { }); // Theme preview - if (themeSelect) { - themeSelect.addEventListener('change', () => { - styleSheet.href = assertNotUndefined(themeSelect.options[themeSelect.selectedIndex].dataset.themePath); - }); - } + const themePreviewCallback = () => { + const themeName = assertNotUndefined(themeSelect.options[themeSelect.selectedIndex].value); + const themeColor = assertNotUndefined(themeColorSelect.options[themeColorSelect.selectedIndex].value); + + styleSheet.href = `/css/${themeName}-${themeColor}.css`; + }; + + themeSelect.addEventListener('change', themePreviewCallback); + themeColorSelect.addEventListener('change', themePreviewCallback); } diff --git a/lib/philomena/users/user.ex b/lib/philomena/users/user.ex index 57c785805..8bb2f6936 100644 --- a/lib/philomena/users/user.ex +++ b/lib/philomena/users/user.ex @@ -357,7 +357,7 @@ defmodule Philomena.Users.User do |> TagList.propagate_tag_list(:watched_tag_list, :watched_tag_ids) |> validate_inclusion( :theme, - ~W(dark-blue dark-red dark-green dark-purple dark-pink dark-yellow dark-cyan dark-grey light-blue light-red light-green light-purple light-pink light-yellow light-cyan light-grey) + ~W(dark-red dark-orange dark-yellow dark-blue dark-green dark-purple dark-cyan dark-pink dark-silver light-red light-orange light-yellow light-blue light-green light-purple light-cyan light-pink light-silver) ) |> validate_inclusion(:images_per_page, 1..50) |> validate_inclusion(:comments_per_page, 1..100) diff --git a/lib/philomena_web/controllers/setting_controller.ex b/lib/philomena_web/controllers/setting_controller.ex index 09c3496a5..f7928bf9f 100644 --- a/lib/philomena_web/controllers/setting_controller.ex +++ b/lib/philomena_web/controllers/setting_controller.ex @@ -9,6 +9,7 @@ defmodule PhilomenaWeb.SettingController do def edit(conn, _params) do changeset = (conn.assigns.current_user || %User{}) + |> assign_theme() |> TagList.assign_tag_list(:watched_tag_ids, :watched_tag_list) |> Users.change_user() @@ -57,13 +58,21 @@ defmodule PhilomenaWeb.SettingController do ) end - defp determine_theme(%{"theme" => "light", "light_theme" => name} = attrs) when name != nil, - do: Map.replace(attrs, "theme", name) + defp assign_theme(%{theme: theme} = user) do + [theme_name, theme_color] = String.split(theme, "-") - defp determine_theme(%{"dark_theme" => name} = attrs) when name != nil, - do: Map.replace(attrs, "theme", name) + user + |> Map.put(:theme_name, theme_name) + |> Map.put(:theme_color, theme_color) + end + + defp assign_theme(_), do: assign_theme(%{theme: "dark-blue"}) + + defp determine_theme(%{"theme_name" => name, "theme_color" => color} = attrs) + when name != nil and color != nil, + do: Map.put(attrs, "theme", "#{name}-#{color}") - defp determine_theme(attrs), do: Map.replace(attrs, "theme", "dark-blue") + defp determine_theme(attrs), do: Map.put(attrs, "theme", "dark-blue") defp maybe_update_user(conn, nil, _user_params), do: {:ok, conn} diff --git a/lib/philomena_web/templates/layout/app.html.slime b/lib/philomena_web/templates/layout/app.html.slime index f0805313d..2ca353fed 100644 --- a/lib/philomena_web/templates/layout/app.html.slime +++ b/lib/philomena_web/templates/layout/app.html.slime @@ -13,9 +13,9 @@ html lang="en" =<> site_name() link rel="preconnect" href="https://#{cdn_host()}" link rel="stylesheet" href=~p"/css/application.css" - link rel="stylesheet" href=stylesheet_path(@conn, @current_user) + link#js-theme-stylesheet rel="stylesheet" href=stylesheet_path(@conn, @current_user) = if is_nil(@current_user) do - link rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)" + link#js-theme-stylesheet rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)" link rel="icon" href=~p"/favicon.ico" type="image/x-icon" link rel="icon" href=~p"/favicon.svg" type="image/svg+xml" link rel="search" type="application/opensearchdescription+xml" title="Derpibooru" href="/opensearch.xml" diff --git a/lib/philomena_web/templates/setting/edit.html.slime b/lib/philomena_web/templates/setting/edit.html.slime index aa10ae2dc..524ee17c8 100644 --- a/lib/philomena_web/templates/setting/edit.html.slime +++ b/lib/philomena_web/templates/setting/edit.html.slime @@ -83,22 +83,16 @@ h1 Content Settings ' For 1080p monitors, try 24. .field label Theme - select.input#js-theme-selector name="user[theme]" id="user_theme" - option value="dark" Dark - option value="light" Light + .with-error + => select f, :theme_name, themes(), class: "input" + = error_tag f, :theme_name .fieldlabel: i General appearance of the theme - .field#js-theme-dark + .field label Theme color .with-error - => select f, :dark_theme, theme_options(), class: "input" - = error_tag f, :dark_theme + => select f, :theme_color, theme_colors(), class: "input" + = error_tag f, :theme_color .fieldlabel: i Color of the theme, don't forget to save settings to apply the theme - .field.hidden#js-theme-light - label Theme color - .with-error - => select f, :light_theme, light_theme_options(), class: "input" - = error_tag f, :light_theme - .fieldlabel: i Preview themes by selecting one from the dropdown. Saving sets the currently selected theme. .field => label f, :scale_large_images .with-error diff --git a/lib/philomena_web/views/layout_view.ex b/lib/philomena_web/views/layout_view.ex index b30a0d8ca..db166710c 100644 --- a/lib/philomena_web/views/layout_view.ex +++ b/lib/philomena_web/views/layout_view.ex @@ -71,22 +71,24 @@ defmodule PhilomenaWeb.LayoutView do def stylesheet_path(conn, %{theme: theme}) when theme in [ - "dark-blue", "dark-red", + "dark-orange", + "dark-yellow", + "dark-blue", "dark-green", "dark-purple", - "dark-pink", - "dark-yellow", "dark-cyan", - "dark-grey", - "light-blue", + "dark-pink", + "dark-silver", "light-red", + "light-orange", + "light-yellow", + "light-blue", "light-green", "light-purple", - "light-pink", - "light-yellow", "light-cyan", - "light-grey" + "light-pink", + "light-silver" ], do: static_path(conn, "/css/#{theme}.css") diff --git a/lib/philomena_web/views/setting_view.ex b/lib/philomena_web/views/setting_view.ex index a9eb68cd2..63030bb23 100644 --- a/lib/philomena_web/views/setting_view.ex +++ b/lib/philomena_web/views/setting_view.ex @@ -1,103 +1,24 @@ defmodule PhilomenaWeb.SettingView do use PhilomenaWeb, :view - def theme_options do + def themes do [ - [ - key: "Red", - value: "dark-red", - data: [theme_path: ~p"/css/dark-red.css"] - ], - [ - key: "Orange", - value: "dark-orange", - data: [theme_path: ~p"/css/dark-orange.css"] - ], - [ - key: "Yellow", - value: "dark-yellow", - data: [theme_path: ~p"/css/dark-yellow.css"] - ], - [ - key: "Green", - value: "dark-green", - data: [theme_path: ~p"/css/dark-green.css"] - ], - [ - key: "Blue", - value: "dark-blue", - data: [theme_path: ~p"/css/dark-blue.css"] - ], - [ - key: "Purple", - value: "dark-purple", - data: [theme_path: ~p"/css/dark-purple.css"] - ], - [ - key: "Cyan", - value: "dark-cyan", - data: [theme_path: ~p"/css/dark-cyan.css"] - ], - [ - key: "Pink", - value: "dark-pink", - data: [theme_path: ~p"/css/dark-pink.css"] - ], - [ - key: "Grey", - value: "dark-grey", - data: [theme_path: ~p"/css/dark-grey.css"] - ] + Dark: "dark", + Light: "light" ] end - def light_theme_options do + def theme_colors do [ - [ - key: "Red", - value: "light-red", - data: [theme_path: ~p"/css/light-red.css"] - ], - [ - key: "Orange", - value: "light-orange", - data: [theme_path: ~p"/css/light-orange.css"] - ], - [ - key: "Yellow", - value: "light-yellow", - data: [theme_path: ~p"/css/light-yellow.css"] - ], - [ - key: "Green", - value: "light-green", - data: [theme_path: ~p"/css/light-green.css"] - ], - [ - key: "Blue", - value: "light-blue", - data: [theme_path: ~p"/css/light-blue.css"] - ], - [ - key: "Purple", - value: "light-purple", - data: [theme_path: ~p"/css/light-purple.css"] - ], - [ - key: "Cyan", - value: "light-cyan", - data: [theme_path: ~p"/css/light-cyan.css"] - ], - [ - key: "Pink", - value: "light-pink", - data: [theme_path: ~p"/css/light-pink.css"] - ], - [ - key: "Grey", - value: "light-grey", - data: [theme_path: ~p"/css/light-grey.css"] - ] + Red: "red", + Orange: "orange", + Yellow: "yellow", + Green: "green", + Blue: "blue", + Purple: "purple", + Cyan: "cyan", + Pink: "pink", + "Silver/Charcoal": "silver" ] end From ad1de092220928ceed41a425ab9ab496e210ea55 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 3 Jul 2024 21:32:38 +0200 Subject: [PATCH 58/65] suppress background task output in development --- docker/app/run-development | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/app/run-development b/docker/app/run-development index 39900c76c..dd7bcae41 100755 --- a/docker/app/run-development +++ b/docker/app/run-development @@ -15,11 +15,11 @@ export CARGO_HOME=/srv/philomena/.cargo background() { while :; do - mix run -e 'Philomena.Release.update_channels()' - mix run -e 'Philomena.Release.verify_artist_links()' - mix run -e 'Philomena.Release.update_stats()' - mix run -e 'Philomena.Release.clean_moderation_logs()' - mix run -e 'Philomena.Release.generate_autocomplete()' + mix run -e 'Philomena.Release.update_channels()' > /dev/null + mix run -e 'Philomena.Release.verify_artist_links()' > /dev/null + mix run -e 'Philomena.Release.update_stats()' > /dev/null + mix run -e 'Philomena.Release.clean_moderation_logs()' > /dev/null + mix run -e 'Philomena.Release.generate_autocomplete()' > /dev/null sleep 300 done From 2b13d6ebc313e6e16bf819b0d424a0cfe0299fcb Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 3 Jul 2024 21:47:01 +0200 Subject: [PATCH 59/65] bump typescript-eslint --- assets/package-lock.json | 346 +++++++++++++++++++-------------------- assets/package.json | 2 +- 2 files changed, 174 insertions(+), 174 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index 80e43891b..95dcdbf55 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -17,7 +17,7 @@ "postcss-mixins": "^10.0.1", "postcss-simple-vars": "^7.0.1", "typescript": "^5.4", - "typescript-eslint": "8.0.0-alpha.30", + "typescript-eslint": "8.0.0-alpha.39", "vite": "^5.2" }, "devDependencies": { @@ -207,9 +207,9 @@ "dev": true }, "node_modules/@csstools/color-helpers": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.2.0.tgz", - "integrity": "sha512-hJJrSBzbfGxUsaR6X4Bzd/FLx0F1ulKnR5ljY9AiXCtsR+H+zSWQDFWlKES1BRaVZTDHLpIIHS9K2o0h+JLlrg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-4.2.1.tgz", + "integrity": "sha512-CEypeeykO9AN7JWkr1OEOQb0HRzZlPWGwV0Ya6DuVgFdDi6g3ma/cPZ5ZPZM4AWQikDpq/0llnGGlIL+j8afzw==", "funding": [ { "type": "github", @@ -225,9 +225,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.2.2.tgz", - "integrity": "sha512-0owrl7AruDRKAxoSIW8XzJdz7GnuW3AOj4rYLfmXsoKIX2ZZzttzGXoiC8n8V08X7wIBlEWWVB4C8fAN18+I6Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.2.3.tgz", + "integrity": "sha512-rlOh81K3CvtY969Od5b1h29YT6MpCHejMCURKrRrXFeCpz67HGaBNvBmWT5S7S+CKn+V7KJ+qxSmK8jNd/aZWA==", "funding": [ { "type": "github", @@ -242,14 +242,14 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-parser-algorithms": "^2.7.0", + "@csstools/css-tokenizer": "^2.3.2" } }, "node_modules/@csstools/css-color-parser": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-2.0.2.tgz", - "integrity": "sha512-Agx2YmxTcZ7TfB7KNZQ+iekaxbWSdblvtA35aTwE3KfuYyjOlCg3P4KGGdQF/cjm1pHWVSBo5duF/BRfZ8s07A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-2.0.3.tgz", + "integrity": "sha512-Qqhb5I/gEh1wI4brf6Kmy0Xn4J1IqO8OTDKWGRsBYtL4bGkHcV9i0XI2Mmo/UYFtSRoXW/RmKTcMh6sCI433Cw==", "funding": [ { "type": "github", @@ -261,21 +261,21 @@ } ], "dependencies": { - "@csstools/color-helpers": "^4.2.0", - "@csstools/css-calc": "^1.2.2" + "@csstools/color-helpers": "^4.2.1", + "@csstools/css-calc": "^1.2.3" }, "engines": { "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-parser-algorithms": "^2.7.0", + "@csstools/css-tokenizer": "^2.3.2" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz", - "integrity": "sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.0.tgz", + "integrity": "sha512-qvBMcOU/uWFCH/VO0MYe0AMs0BGMWAt6FTryMbFIKYtZtVnqTZtT8ktv5o718llkaGZWomJezJZjq3vJDHeJNQ==", "funding": [ { "type": "github", @@ -290,13 +290,13 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-tokenizer": "^2.3.2" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.1.tgz", - "integrity": "sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.3.tgz", + "integrity": "sha512-fTaF0vRcXVJ4cmwg8nHofydDjitKMDBzC8cCu+O/Lg13C4PdkC15GVjGpbmWauOOnhomVSTg5I5LpLJFJE2Hfw==", "funding": [ { "type": "github", @@ -312,9 +312,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.11.tgz", - "integrity": "sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==", + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.12.tgz", + "integrity": "sha512-t1/CdyVJzOQUiGUcIBXRzTAkWTFPxiPnoKwowKW2z9Uj78c2bBWI/X94BeVfUwVq1xtCjD7dnO8kS6WONgp8Jw==", "dev": true, "funding": [ { @@ -330,8 +330,8 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1" + "@csstools/css-parser-algorithms": "^2.7.0", + "@csstools/css-tokenizer": "^2.3.2" } }, "node_modules/@csstools/postcss-progressive-custom-properties": { @@ -359,9 +359,9 @@ } }, "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.16.tgz", - "integrity": "sha512-TSM8fVqJkT8JZDranZPnkpxjU/Q1sNR192lXMND+EcKOUjYa6uYpGSfHgjnWjCRiBSciettS+sL7y9wmnas7qQ==", + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-2.0.17.tgz", + "integrity": "sha512-EVckAtG8bocItZflXLJ50Su+gwg/4Jhkz1BztyNsT0/svwS6QMAeLjyUA75OsgtejNWQHvBMWna4xc9LCqdjrQ==", "funding": [ { "type": "github", @@ -373,9 +373,9 @@ } ], "dependencies": { - "@csstools/css-color-parser": "^2.0.2", - "@csstools/css-parser-algorithms": "^2.6.3", - "@csstools/css-tokenizer": "^2.3.1", + "@csstools/css-color-parser": "^2.0.3", + "@csstools/css-parser-algorithms": "^2.7.0", + "@csstools/css-tokenizer": "^2.3.2", "@csstools/postcss-progressive-custom-properties": "^3.2.0", "@csstools/utilities": "^1.0.0" }, @@ -810,21 +810,21 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.16.0.tgz", - "integrity": "sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz", + "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==", "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", - "minimatch": "^3.0.5" + "minimatch": "^3.1.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -853,9 +853,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.5.0.tgz", - "integrity": "sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", + "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1261,9 +1261,9 @@ } }, "node_modules/@testing-library/dom": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz", - "integrity": "sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.3.0.tgz", + "integrity": "sha512-pT/TYB2+IyMYkkB6lqpkzD7VFbsR0JBJtflK3cS68sCNWxmOhWwRm1XvVHlseNEorsNcxkYsb4sRDV3aNIpttg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", @@ -1409,9 +1409,9 @@ } }, "node_modules/@types/node": { - "version": "20.14.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", - "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", "dependencies": { "undici-types": "~5.26.4" } @@ -1453,15 +1453,15 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.30.tgz", - "integrity": "sha512-2CBUupdkfbE3eATph4QeZejvT+M+1bVur+zXlVx09WN31phap51ps/qemeclnCbGEz6kTgBDmScrr9XmmF8/Pg==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.39.tgz", + "integrity": "sha512-ILv1vDA8M9ah1vzYpnOs4UOLRdB63Ki/rsxedVikjMLq68hFfpsDR25bdMZ4RyUkzLJwOhcg3Jujm/C1nupXKA==", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.0-alpha.30", - "@typescript-eslint/type-utils": "8.0.0-alpha.30", - "@typescript-eslint/utils": "8.0.0-alpha.30", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", + "@typescript-eslint/scope-manager": "8.0.0-alpha.39", + "@typescript-eslint/type-utils": "8.0.0-alpha.39", + "@typescript-eslint/utils": "8.0.0-alpha.39", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.39", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1485,14 +1485,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.30.tgz", - "integrity": "sha512-tAYgFmgXU1MlCK3nbblUvJlDSibBvxtAQXGrF3IG0KmnRza9FXILZifHWL0rrwacDn40K53K607Fk2QkMjiGgw==", - "dependencies": { - "@typescript-eslint/scope-manager": "8.0.0-alpha.30", - "@typescript-eslint/types": "8.0.0-alpha.30", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.30", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.39.tgz", + "integrity": "sha512-5k+pwV91plJojHgZkWlq4/TQdOrnEaeSvt48V0m8iEwdMJqX/63BXYxy8BUOSghWcjp05s73vy9HJjovAKmHkQ==", + "dependencies": { + "@typescript-eslint/scope-manager": "8.0.0-alpha.39", + "@typescript-eslint/types": "8.0.0-alpha.39", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.39", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.39", "debug": "^4.3.4" }, "engines": { @@ -1512,12 +1512,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.30.tgz", - "integrity": "sha512-FGW/iPWGyPFamAVZ60oCAthMqQrqafUGebF8UKuq/ha+e9SVG6YhJoRzurlQXOVf8dHfOhJ0ADMXyFnMc53clg==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.39.tgz", + "integrity": "sha512-HCBlKQROY+JIgWolucdFMj1W3VUnnIQTdxAhxJTAj3ix2nASmvKIFgrdo5KQMrXxQj6tC4l3zva10L+s0dUIIw==", "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.30", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.30" + "@typescript-eslint/types": "8.0.0-alpha.39", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.39" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1528,12 +1528,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.30.tgz", - "integrity": "sha512-FrnhlCKEKZKRbpDviHkIU9tayIUGTOfa+SjvrRv6p/AJIUv6QT8oRboRjLH/cCuwUEbM0k5UtRWYug4albHUqQ==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.39.tgz", + "integrity": "sha512-alO13fRU6yVeJbwl9ESI3AYhq5dQdz3Dpd0I5B4uezs2lvgYp44dZsj5hWyPz/kL7JFEsjbn+4b/CZA0OQJzjA==", "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.0-alpha.30", - "@typescript-eslint/utils": "8.0.0-alpha.30", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.39", + "@typescript-eslint/utils": "8.0.0-alpha.39", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1551,9 +1551,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.30.tgz", - "integrity": "sha512-4WzLlw27SO9pK9UFj/Hu7WGo8WveT0SEiIpFVsV2WwtQmLps6kouwtVCB8GJPZKJyurhZhcqCoQVQFmpv441Vg==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.39.tgz", + "integrity": "sha512-yINN7j0/+S1VGSp0IgH52oQvUx49vkOug6xbrDA/9o+U55yCAQKSvYWvzYjNa+SZE3hXI0zwvYtMVsIAAMmKIQ==", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1563,12 +1563,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.30.tgz", - "integrity": "sha512-WSXbc9ZcXI+7yC+6q95u77i8FXz6HOLsw3ST+vMUlFy1lFbXyFL/3e6HDKQCm2Clt0krnoCPiTGvIn+GkYPn4Q==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.39.tgz", + "integrity": "sha512-S8gREuP8r8PCxGegeojeXntx0P50ul9YH7c7JYpbLIIsEPNr5f7UHlm+I1NUbL04CBin4kvZ60TG4eWr/KKN9A==", "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.30", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.30", + "@typescript-eslint/types": "8.0.0-alpha.39", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.39", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1598,9 +1598,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1612,14 +1612,14 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.30.tgz", - "integrity": "sha512-rfhqfLqFyXhHNDwMnHiVGxl/Z2q/3guQ1jLlGQ0hi9Rb7inmwz42crM+NnLPR+2vEnwyw1P/g7fnQgQ3qvFx4g==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.39.tgz", + "integrity": "sha512-Nr2PrlfNhrNQTlFHlD7XJdTGw/Vt8qY44irk6bfjn9LxGdSG5e4c1R2UN6kvGMhhx20DBPbM7q3Z3r+huzmL1w==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.0-alpha.30", - "@typescript-eslint/types": "8.0.0-alpha.30", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.30" + "@typescript-eslint/scope-manager": "8.0.0-alpha.39", + "@typescript-eslint/types": "8.0.0-alpha.39", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.39" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1633,11 +1633,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.30.tgz", - "integrity": "sha512-XZuNurZxBqmr6ZIRIwWFq7j5RZd6ZlkId/HZEWyfciK+CWoyOxSF9Pv2VXH9Rlu2ZG2PfbhLz2Veszl4Pfn7yA==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.39.tgz", + "integrity": "sha512-DVJ0UdhucZy+/1GlIy7FX2+CFhCeNAi4VwaEAe7u2UDenQr9/kGqvzx00UlpWibmEVDw4KsPOI7Aqa1+2Vqfmw==", "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.30", + "@typescript-eslint/types": "8.0.0-alpha.39", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1799,9 +1799,9 @@ } }, "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, "engines": { "node": ">=12.20" @@ -1931,9 +1931,9 @@ "deprecated": "Use your platform's native atob() and btoa() methods instead" }, "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "bin": { "acorn": "bin/acorn" }, @@ -2181,9 +2181,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001636", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", - "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", + "version": "1.0.30001640", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz", + "integrity": "sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==", "funding": [ { "type": "opencollective", @@ -2529,9 +2529,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.810", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.810.tgz", - "integrity": "sha512-Kaxhu4T7SJGpRQx99tq216gCq2nMxJo+uuT6uzz9l8TVN2stL7M06MIIXAtr9jsrLs2Glflgf2vMQRepxawOdQ==" + "version": "1.4.816", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz", + "integrity": "sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2645,15 +2645,15 @@ } }, "node_modules/eslint": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.5.0.tgz", - "integrity": "sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.6.0.tgz", + "integrity": "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/config-array": "^0.16.0", + "@eslint/config-array": "^0.17.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.5.0", + "@eslint/js": "9.6.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -2664,7 +2664,7 @@ "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.0.1", "eslint-visitor-keys": "^4.0.0", - "espree": "^10.0.1", + "espree": "^10.1.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2719,13 +2719,13 @@ } }, "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", - "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", + "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1" + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2736,9 +2736,9 @@ } }, "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", - "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", + "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2749,13 +2749,13 @@ } }, "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", - "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", + "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2777,15 +2777,15 @@ } }, "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", - "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", + "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1" + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2799,12 +2799,12 @@ } }, "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", - "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", + "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/types": "7.15.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2837,9 +2837,9 @@ } }, "node_modules/eslint-plugin-vitest/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3326,9 +3326,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, "dependencies": { "agent-base": "^7.0.2", @@ -3526,9 +3526,9 @@ } }, "node_modules/istanbul-lib-source-maps": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", - "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", @@ -4472,20 +4472,20 @@ } }, "node_modules/pkg-types": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", - "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.3.tgz", + "integrity": "sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==", "dev": true, "dependencies": { "confbox": "^0.1.7", - "mlly": "^1.7.0", + "mlly": "^1.7.1", "pathe": "^1.1.2" } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "funding": [ { "type": "opencollective", @@ -4502,7 +4502,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -5451,9 +5451,9 @@ } }, "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5463,13 +5463,13 @@ } }, "node_modules/typescript-eslint": { - "version": "8.0.0-alpha.30", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.30.tgz", - "integrity": "sha512-/vGhBMsK1TpadQh1eQ02c5pyiPGmKR9cVzX5C9plZ+LC0HPLpWoJbbTVfQN7BkIK7tUxDt2BFr3pFL5hDDrx7g==", + "version": "8.0.0-alpha.39", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.39.tgz", + "integrity": "sha512-bsuR1BVJfHr7sBh7Cca962VPIcP+5UWaIa/+6PpnFZ+qtASjGTxKWIF5dG2o73BX9NsyqQfvRWujb3M9CIoRXA==", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.0.0-alpha.30", - "@typescript-eslint/parser": "8.0.0-alpha.30", - "@typescript-eslint/utils": "8.0.0-alpha.30" + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.39", + "@typescript-eslint/parser": "8.0.0-alpha.39", + "@typescript-eslint/utils": "8.0.0-alpha.39" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5504,9 +5504,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "funding": [ { "type": "opencollective", @@ -5556,12 +5556,12 @@ "dev": true }, "node_modules/vite": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz", - "integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", + "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.38", + "postcss": "^8.4.39", "rollup": "^4.13.0" }, "bin": { @@ -5892,9 +5892,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, diff --git a/assets/package.json b/assets/package.json index 2bae17fee..8fe6f2074 100644 --- a/assets/package.json +++ b/assets/package.json @@ -14,7 +14,7 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@types/postcss-mixins": "^9.0.5", "@types/web": "^0.0.148", - "typescript-eslint": "8.0.0-alpha.30", + "typescript-eslint": "8.0.0-alpha.39", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", "eslint": "^9.4.0", From db88434d80181f5695d000b230219bf50290a866 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 3 Jul 2024 22:00:12 +0200 Subject: [PATCH 60/65] fix theme select for prod --- assets/js/settings.ts | 7 +++++- .../templates/setting/edit.html.slime | 1 + lib/philomena_web/views/setting_view.ex | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/assets/js/settings.ts b/assets/js/settings.ts index e4ab89278..0bc8a9d7f 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -13,6 +13,11 @@ export function setupSettings() { const localCheckboxes = $$('[data-tab="local"] input[type="checkbox"]'); const themeSelect = assertNotNull($('#user_theme_name')); const themeColorSelect = assertNotNull($('#user_theme_color')); + const themePaths: Record = JSON.parse( + assertNotUndefined( + assertNotNull($('#js-theme-paths')).dataset.themePaths + ) + ); const styleSheet = assertNotNull($('#js-theme-stylesheet')); // Local settings @@ -27,7 +32,7 @@ export function setupSettings() { const themeName = assertNotUndefined(themeSelect.options[themeSelect.selectedIndex].value); const themeColor = assertNotUndefined(themeColorSelect.options[themeColorSelect.selectedIndex].value); - styleSheet.href = `/css/${themeName}-${themeColor}.css`; + styleSheet.href = themePaths[`${themeName}-${themeColor}`]; }; themeSelect.addEventListener('change', themePreviewCallback); diff --git a/lib/philomena_web/templates/setting/edit.html.slime b/lib/philomena_web/templates/setting/edit.html.slime index 524ee17c8..cef78b679 100644 --- a/lib/philomena_web/templates/setting/edit.html.slime +++ b/lib/philomena_web/templates/setting/edit.html.slime @@ -93,6 +93,7 @@ h1 Content Settings => select f, :theme_color, theme_colors(), class: "input" = error_tag f, :theme_color .fieldlabel: i Color of the theme, don't forget to save settings to apply the theme + .hidden#js-theme-paths data-theme-paths=theme_paths_json() .field => label f, :scale_large_images .with-error diff --git a/lib/philomena_web/views/setting_view.ex b/lib/philomena_web/views/setting_view.ex index 63030bb23..59d9a74c8 100644 --- a/lib/philomena_web/views/setting_view.ex +++ b/lib/philomena_web/views/setting_view.ex @@ -22,6 +22,31 @@ defmodule PhilomenaWeb.SettingView do ] end + def theme_paths_json do + Jason.encode!( + %{ + "dark-red": ~p"/css/dark-red.css", + "dark-orange": ~p"/css/dark-orange.css", + "dark-yellow": ~p"/css/dark-yellow.css", + "dark-blue": ~p"/css/dark-blue.css", + "dark-green": ~p"/css/dark-green.css", + "dark-purple": ~p"/css/dark-purple.css", + "dark-cyan": ~p"/css/dark-cyan.css", + "dark-pink": ~p"/css/dark-pink.css", + "dark-silver": ~p"/css/dark-silver.css", + "light-red": ~p"/css/light-red.css", + "light-orange": ~p"/css/light-orange.css", + "light-yellow": ~p"/css/light-yellow.css", + "light-blue": ~p"/css/light-blue.css", + "light-green": ~p"/css/light-green.css", + "light-purple": ~p"/css/light-purple.css", + "light-cyan": ~p"/css/light-cyan.css", + "light-pink": ~p"/css/light-pink.css", + "light-silver": ~p"/css/light-silver.css" + } + ) + end + def scale_options do [ [key: "Load full images on image pages", value: "false"], From 4ae468142da404639f972468b9bdacd21db1c043 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 3 Jul 2024 22:00:58 +0200 Subject: [PATCH 61/65] mix format --- lib/philomena_web/views/setting_view.ex | 42 ++++++++++++------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/lib/philomena_web/views/setting_view.ex b/lib/philomena_web/views/setting_view.ex index 59d9a74c8..929df81dd 100644 --- a/lib/philomena_web/views/setting_view.ex +++ b/lib/philomena_web/views/setting_view.ex @@ -23,28 +23,26 @@ defmodule PhilomenaWeb.SettingView do end def theme_paths_json do - Jason.encode!( - %{ - "dark-red": ~p"/css/dark-red.css", - "dark-orange": ~p"/css/dark-orange.css", - "dark-yellow": ~p"/css/dark-yellow.css", - "dark-blue": ~p"/css/dark-blue.css", - "dark-green": ~p"/css/dark-green.css", - "dark-purple": ~p"/css/dark-purple.css", - "dark-cyan": ~p"/css/dark-cyan.css", - "dark-pink": ~p"/css/dark-pink.css", - "dark-silver": ~p"/css/dark-silver.css", - "light-red": ~p"/css/light-red.css", - "light-orange": ~p"/css/light-orange.css", - "light-yellow": ~p"/css/light-yellow.css", - "light-blue": ~p"/css/light-blue.css", - "light-green": ~p"/css/light-green.css", - "light-purple": ~p"/css/light-purple.css", - "light-cyan": ~p"/css/light-cyan.css", - "light-pink": ~p"/css/light-pink.css", - "light-silver": ~p"/css/light-silver.css" - } - ) + Jason.encode!(%{ + "dark-red": ~p"/css/dark-red.css", + "dark-orange": ~p"/css/dark-orange.css", + "dark-yellow": ~p"/css/dark-yellow.css", + "dark-blue": ~p"/css/dark-blue.css", + "dark-green": ~p"/css/dark-green.css", + "dark-purple": ~p"/css/dark-purple.css", + "dark-cyan": ~p"/css/dark-cyan.css", + "dark-pink": ~p"/css/dark-pink.css", + "dark-silver": ~p"/css/dark-silver.css", + "light-red": ~p"/css/light-red.css", + "light-orange": ~p"/css/light-orange.css", + "light-yellow": ~p"/css/light-yellow.css", + "light-blue": ~p"/css/light-blue.css", + "light-green": ~p"/css/light-green.css", + "light-purple": ~p"/css/light-purple.css", + "light-cyan": ~p"/css/light-cyan.css", + "light-pink": ~p"/css/light-pink.css", + "light-silver": ~p"/css/light-silver.css" + }) end def scale_options do From 33ede2722b6bd2c1b7ce29db74ff3cfbeea1aa51 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 3 Jul 2024 22:54:14 +0200 Subject: [PATCH 62/65] prettier --- assets/.prettierrc.yml | 9 +- assets/eslint.config.js | 55 ++- assets/js/__tests__/imagesclientside.spec.ts | 25 +- assets/js/__tests__/input-duplicator.spec.ts | 7 +- assets/js/__tests__/ujs.spec.ts | 95 ++--- assets/js/__tests__/upload.spec.ts | 28 +- assets/js/autocomplete.js | 72 ++-- assets/js/booru.js | 33 +- assets/js/boorujs.js | 90 +++-- assets/js/burger.ts | 7 +- assets/js/captcha.ts | 6 +- assets/js/comment.js | 59 +-- assets/js/duplicate_reports.ts | 2 +- assets/js/fp.ts | 30 +- assets/js/galleries.ts | 17 +- assets/js/graph.ts | 10 +- assets/js/image_expansion.js | 53 ++- assets/js/imagesclientside.ts | 44 +-- assets/js/input-duplicator.ts | 9 +- assets/js/interactions.js | 148 ++++---- assets/js/markdowntoolbar.js | 133 ++++--- assets/js/misc.ts | 16 +- assets/js/notifications.ts | 10 +- assets/js/pmwarning.ts | 3 +- assets/js/preview.js | 13 +- assets/js/query/__tests__/date.spec.ts | 4 +- assets/js/query/__tests__/user.spec.ts | 8 +- assets/js/query/boolean.ts | 4 +- assets/js/query/date.ts | 30 +- assets/js/query/fields.ts | 23 +- assets/js/query/lex.ts | 38 +- assets/js/query/literal.ts | 11 +- assets/js/query/matcher.ts | 8 +- assets/js/query/number.ts | 2 +- assets/js/query/parse.ts | 9 +- assets/js/query/term.ts | 6 +- assets/js/query/user.ts | 11 +- assets/js/quick-tag.js | 64 ++-- assets/js/search.ts | 16 +- assets/js/settings.ts | 7 +- assets/js/shortcuts.ts | 56 ++- assets/js/slider.ts | 29 +- assets/js/sources.ts | 2 +- assets/js/staffhider.ts | 2 +- assets/js/tags.ts | 70 ++-- assets/js/tagsinput.js | 16 +- assets/js/tagsmisc.ts | 6 +- assets/js/timeago.ts | 32 +- assets/js/ujs.ts | 28 +- assets/js/upload.js | 93 ++--- assets/js/utils/__tests__/array.spec.ts | 65 ++-- assets/js/utils/__tests__/dom.spec.ts | 76 +--- assets/js/utils/__tests__/draggable.spec.ts | 24 +- assets/js/utils/__tests__/events.spec.ts | 4 +- assets/js/utils/__tests__/image.spec.ts | 94 ++--- .../__tests__/local-autocompleter.spec.ts | 6 +- assets/js/utils/__tests__/requests.spec.ts | 10 +- assets/js/utils/__tests__/store.spec.ts | 14 +- assets/js/utils/__tests__/tag.spec.ts | 36 +- assets/js/utils/dom.ts | 49 +-- assets/js/utils/draggable.ts | 17 +- assets/js/utils/events.ts | 34 +- assets/js/utils/image.ts | 7 +- assets/js/utils/lerp.ts | 7 +- assets/js/utils/local-autocompleter.ts | 23 +- assets/js/utils/requests.ts | 4 +- assets/js/utils/store.ts | 11 +- assets/js/utils/tag.ts | 16 +- assets/js/when-ready.ts | 62 ++-- assets/package-lock.json | 345 +++++++++++++++--- assets/package.json | 7 +- assets/test/fix-event-listeners.ts | 2 +- assets/test/mock-storage.ts | 16 +- assets/test/vitest-setup.ts | 3 +- assets/types/ujs.ts | 2 +- assets/vite.config.ts | 40 +- 76 files changed, 1399 insertions(+), 1124 deletions(-) diff --git a/assets/.prettierrc.yml b/assets/.prettierrc.yml index 546aecb08..05ab046f5 100644 --- a/assets/.prettierrc.yml +++ b/assets/.prettierrc.yml @@ -2,6 +2,13 @@ tabWidth: 2 useTabs: false printWidth: 120 semi: true -singleQuote: false +singleQuote: true bracketSpacing: true endOfLine: lf +quoteProps: as-needed +trailingComma: all +arrowParens: always +overrides: + - files: "*.css" + options: + singleQuote: false diff --git a/assets/eslint.config.js b/assets/eslint.config.js index 166da7586..c927efb6c 100644 --- a/assets/eslint.config.js +++ b/assets/eslint.config.js @@ -1,9 +1,11 @@ import tsEslint from 'typescript-eslint'; import vitestPlugin from 'eslint-plugin-vitest'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; import globals from 'globals'; export default tsEslint.config( ...tsEslint.configs.recommended, + eslintPluginPrettierRecommended, { name: 'PhilomenaConfig', files: ['**/*.js', '**/*.ts'], @@ -12,24 +14,22 @@ export default tsEslint.config( sourceType: 'module', parserOptions: { ecmaVersion: 6, - sourceType: 'module' + sourceType: 'module', }, globals: { - ...globals.browser - } + ...globals.browser, + }, }, rules: { 'accessor-pairs': 2, 'array-bracket-spacing': 0, 'array-callback-return': 2, 'arrow-body-style': 0, - 'arrow-parens': [2, 'as-needed'], 'arrow-spacing': 2, 'block-scoped-var': 2, 'block-spacing': 2, - 'brace-style': [2, 'stroustrup', {allowSingleLine: true}], 'callback-return': 0, - camelcase: [2, {allow: ['camo_url', 'spoiler_image_uri', 'image_ids']}], + camelcase: [2, { allow: ['camo_url', 'spoiler_image_uri', 'image_ids'] }], 'class-methods-use-this': 0, 'comma-dangle': [2, 'only-multiline'], 'comma-spacing': 2, @@ -42,7 +42,7 @@ export default tsEslint.config( curly: [2, 'multi-line', 'consistent'], 'default-case': 2, 'dot-location': [2, 'property'], - 'dot-notation': [2, {allowKeywords: true}], + 'dot-notation': [2, { allowKeywords: true }], 'eol-last': 2, eqeqeq: 2, 'func-call-spacing': 0, @@ -56,7 +56,6 @@ export default tsEslint.config( 'id-blacklist': 0, 'id-length': 0, 'id-match': 2, - indent: [2, 2, {SwitchCase: 1, VariableDeclarator: {var: 2, let: 2, const: 3}}], 'init-declarations': 0, 'jsx-quotes': 0, 'key-spacing': 0, @@ -110,7 +109,7 @@ export default tsEslint.config( 'no-extra-bind': 2, 'no-extra-boolean-cast': 2, 'no-extra-label': 2, - 'no-extra-parens': [2, 'all', {nestedBinaryExpressions: false}], + 'no-extra-parens': [2, 'all', { nestedBinaryExpressions: false }], 'no-extra-semi': 2, 'no-fallthrough': 2, 'no-floating-decimal': 2, @@ -136,7 +135,7 @@ export default tsEslint.config( 'no-mixed-spaces-and-tabs': 2, 'no-multi-spaces': 0, 'no-multi-str': 2, - 'no-multiple-empty-lines': [2, {max: 3, maxBOF: 0, maxEOF: 1}], + 'no-multiple-empty-lines': [2, { max: 3, maxBOF: 0, maxEOF: 1 }], 'no-native-reassign': 2, 'no-negated-condition': 0, 'no-negated-in-lhs': 2, @@ -190,9 +189,9 @@ export default tsEslint.config( 'no-unreachable': 2, 'no-unsafe-finally': 2, 'no-unsafe-negation': 2, - 'no-unused-expressions': [2, {allowShortCircuit: true, allowTernary: true}], + 'no-unused-expressions': [2, { allowShortCircuit: true, allowTernary: true }], 'no-unused-labels': 2, - 'no-unused-vars': [2, {vars: 'all', args: 'after-used', varsIgnorePattern: '^_', argsIgnorePattern: '^_'}], + 'no-unused-vars': [2, { vars: 'all', args: 'after-used', varsIgnorePattern: '^_', argsIgnorePattern: '^_' }], 'no-use-before-define': [2, 'nofunc'], 'no-useless-call': 2, 'no-useless-computed-key': 2, @@ -222,21 +221,19 @@ export default tsEslint.config( 'prefer-spread': 0, 'prefer-template': 2, 'quote-props': [2, 'as-needed'], - quotes: [2, 'single'], radix: 2, 'require-jsdoc': 0, 'require-yield': 2, 'rest-spread-spacing': 2, - 'semi-spacing': [2, {before: false, after: true}], + 'semi-spacing': [2, { before: false, after: true }], semi: 2, 'sort-imports': 0, 'sort-keys': 0, 'sort-vars': 0, 'space-before-blocks': [2, 'always'], - 'space-before-function-paren': [2, 'never'], 'space-in-parens': [2, 'never'], 'space-infix-ops': 2, - 'space-unary-ops': [2, {words: true, nonwords: false}], + 'space-unary-ops': [2, { words: true, nonwords: false }], 'spaced-comment': 0, strict: [2, 'function'], 'symbol-description': 2, @@ -251,18 +248,15 @@ export default tsEslint.config( 'yield-star-spacing': 2, yoda: [2, 'never'], }, - ignores: [ - 'js/vendor/*', - 'vite.config.ts' - ] + ignores: ['js/vendor/*', 'vite.config.ts'], }, { files: ['**/*.js'], rules: { '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-unused-expressions': 'off', - '@typescript-eslint/no-unused-vars': 'off' - } + '@typescript-eslint/no-unused-vars': 'off', + }, }, { files: ['**/*.ts'], @@ -271,15 +265,18 @@ export default tsEslint.config( 'no-unused-vars': 'off', 'no-redeclare': 'off', 'no-shadow': 'off', - '@typescript-eslint/no-unused-vars': [2, {vars: 'all', args: 'after-used', varsIgnorePattern: '^_.*', argsIgnorePattern: '^_.*'}], + '@typescript-eslint/no-unused-vars': [ + 2, + { vars: 'all', args: 'after-used', varsIgnorePattern: '^_.*', argsIgnorePattern: '^_.*' }, + ], '@typescript-eslint/no-redeclare': 2, - '@typescript-eslint/no-shadow': 2 - } + '@typescript-eslint/no-shadow': 2, + }, }, { files: ['**/*.spec.ts', '**/test/*.ts'], plugins: { - vitest: vitestPlugin + vitest: vitestPlugin, }, rules: { ...vitestPlugin.configs.recommended.rules, @@ -287,7 +284,7 @@ export default tsEslint.config( 'no-undefined': 'off', 'no-unused-expressions': 0, 'vitest/valid-expect': 0, - '@typescript-eslint/no-unused-expressions': 0 - } - } + '@typescript-eslint/no-unused-expressions': 0, + }, + }, ); diff --git a/assets/js/__tests__/imagesclientside.spec.ts b/assets/js/__tests__/imagesclientside.spec.ts index 3f3feb88a..f4c73ace6 100644 --- a/assets/js/__tests__/imagesclientside.spec.ts +++ b/assets/js/__tests__/imagesclientside.spec.ts @@ -23,11 +23,11 @@ describe('filterNode', () => { `; - return [ element, assertNotNull($('.js-spoiler-info-overlay', element)) ]; + return [element, assertNotNull($('.js-spoiler-info-overlay', element))]; } it('should show image media boxes not matching any filter', () => { - const [ container, spoilerOverlay ] = makeMediaContainer(); + const [container, spoilerOverlay] = makeMediaContainer(); filterNode(container); expect(spoilerOverlay).not.toContainHTML('(Complex Filter)'); @@ -36,7 +36,7 @@ describe('filterNode', () => { }); it('should spoiler media boxes spoilered by a tag filter', () => { - const [ container, spoilerOverlay ] = makeMediaContainer(); + const [container, spoilerOverlay] = makeMediaContainer(); window.booru.spoileredTagList = [1]; filterNode(container); @@ -45,7 +45,7 @@ describe('filterNode', () => { }); it('should spoiler media boxes spoilered by a complex filter', () => { - const [ container, spoilerOverlay ] = makeMediaContainer(); + const [container, spoilerOverlay] = makeMediaContainer(); window.booru.spoileredFilter = parseSearch('id:1'); filterNode(container); @@ -54,7 +54,7 @@ describe('filterNode', () => { }); it('should hide media boxes hidden by a tag filter', () => { - const [ container, spoilerOverlay ] = makeMediaContainer(); + const [container, spoilerOverlay] = makeMediaContainer(); window.booru.hiddenTagList = [1]; filterNode(container); @@ -64,7 +64,7 @@ describe('filterNode', () => { }); it('should hide media boxes hidden by a complex filter', () => { - const [ container, spoilerOverlay ] = makeMediaContainer(); + const [container, spoilerOverlay] = makeMediaContainer(); window.booru.hiddenFilter = parseSearch('id:1'); filterNode(container); @@ -90,12 +90,12 @@ describe('filterNode', () => { element, assertNotNull($('.image-filtered', element)), assertNotNull($('.image-show', element)), - assertNotNull($('.filter-explanation', element)) + assertNotNull($('.filter-explanation', element)), ]; } it('should show image blocks not matching any filter', () => { - const [ container, imageFiltered, imageShow ] = makeImageBlock(); + const [container, imageFiltered, imageShow] = makeImageBlock(); filterNode(container); expect(imageFiltered).toHaveClass('hidden'); @@ -104,7 +104,7 @@ describe('filterNode', () => { }); it('should spoiler image blocks spoilered by a tag filter', () => { - const [ container, imageFiltered, imageShow, filterExplanation ] = makeImageBlock(); + const [container, imageFiltered, imageShow, filterExplanation] = makeImageBlock(); window.booru.spoileredTagList = [1]; filterNode(container); @@ -116,7 +116,7 @@ describe('filterNode', () => { }); it('should spoiler image blocks spoilered by a complex filter', () => { - const [ container, imageFiltered, imageShow, filterExplanation ] = makeImageBlock(); + const [container, imageFiltered, imageShow, filterExplanation] = makeImageBlock(); window.booru.spoileredFilter = parseSearch('id:1'); filterNode(container); @@ -128,7 +128,7 @@ describe('filterNode', () => { }); it('should hide image blocks hidden by a tag filter', () => { - const [ container, imageFiltered, imageShow, filterExplanation ] = makeImageBlock(); + const [container, imageFiltered, imageShow, filterExplanation] = makeImageBlock(); window.booru.hiddenTagList = [1]; filterNode(container); @@ -140,7 +140,7 @@ describe('filterNode', () => { }); it('should hide image blocks hidden by a complex filter', () => { - const [ container, imageFiltered, imageShow, filterExplanation ] = makeImageBlock(); + const [container, imageFiltered, imageShow, filterExplanation] = makeImageBlock(); window.booru.hiddenFilter = parseSearch('id:1'); filterNode(container); @@ -150,7 +150,6 @@ describe('filterNode', () => { expect(filterExplanation).toContainHTML('complex tag expression'); expect(window.booru.imagesWithDownvotingDisabled).toContain('1'); }); - }); describe('initImagesClientside', () => { diff --git a/assets/js/__tests__/input-duplicator.spec.ts b/assets/js/__tests__/input-duplicator.spec.ts index 55233ee79..091155733 100644 --- a/assets/js/__tests__/input-duplicator.spec.ts +++ b/assets/js/__tests__/input-duplicator.spec.ts @@ -5,7 +5,9 @@ import { fireEvent } from '@testing-library/dom'; describe('Input duplicator functionality', () => { beforeEach(() => { - document.documentElement.insertAdjacentHTML('beforeend', `
+ document.documentElement.insertAdjacentHTML( + 'beforeend', + `
3
@@ -16,7 +18,8 @@ describe('Input duplicator functionality', () => {
- `); + `, + ); }); afterEach(() => { diff --git a/assets/js/__tests__/ujs.spec.ts b/assets/js/__tests__/ujs.spec.ts index b5b3d2316..282eb4040 100644 --- a/assets/js/__tests__/ujs.spec.ts +++ b/assets/js/__tests__/ujs.spec.ts @@ -29,7 +29,7 @@ describe('Remote utilities', () => { } describe('a[data-remote]', () => { - const submitA = ({ setMethod }: { setMethod: boolean; }) => { + const submitA = ({ setMethod }: { setMethod: boolean }) => { const a = document.createElement('a'); a.href = mockEndpoint; a.dataset.remote = 'remote'; @@ -51,8 +51,8 @@ describe('Remote utilities', () => { credentials: 'same-origin', headers: { 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'XMLHttpRequest' - } + 'x-requested-with': 'XMLHttpRequest', + }, }); }); @@ -64,21 +64,22 @@ describe('Remote utilities', () => { credentials: 'same-origin', headers: { 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'XMLHttpRequest' - } + 'x-requested-with': 'XMLHttpRequest', + }, }); }); - it('should emit fetchcomplete event', () => new Promise(resolve => { - let a: HTMLAnchorElement | null = null; + it('should emit fetchcomplete event', () => + new Promise((resolve) => { + let a: HTMLAnchorElement | null = null; - addOneShotEventListener('fetchcomplete', event => { - expect(event.target).toBe(a); - resolve(); - }); + addOneShotEventListener('fetchcomplete', (event) => { + expect(event.target).toBe(a); + resolve(); + }); - a = submitA({ setMethod: true }); - })); + a = submitA({ setMethod: true }); + })); }); describe('a[data-method]', () => { @@ -93,24 +94,25 @@ describe('Remote utilities', () => { return a; }; - it('should submit a form with the given action', () => new Promise(resolve => { - addOneShotEventListener('submit', event => { - event.preventDefault(); + it('should submit a form with the given action', () => + new Promise((resolve) => { + addOneShotEventListener('submit', (event) => { + event.preventDefault(); - const target = assertType(event.target, HTMLFormElement); - const [ csrf, method ] = target.querySelectorAll('input'); + const target = assertType(event.target, HTMLFormElement); + const [csrf, method] = target.querySelectorAll('input'); - expect(csrf.name).toBe('_csrf_token'); - expect(csrf.value).toBe(window.booru.csrfToken); + expect(csrf.name).toBe('_csrf_token'); + expect(csrf.value).toBe(window.booru.csrfToken); - expect(method.name).toBe('_method'); - expect(method.value).toBe(mockVerb); + expect(method.name).toBe('_method'); + expect(method.value).toBe(mockVerb); - resolve(); - }); + resolve(); + }); - submitA(); - })); + submitA(); + })); }); describe('form[data-remote]', () => { @@ -167,7 +169,7 @@ describe('Remote utilities', () => { credentials: 'same-origin', headers: { 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'XMLHttpRequest' + 'x-requested-with': 'XMLHttpRequest', }, body: new FormData(), }); @@ -183,25 +185,26 @@ describe('Remote utilities', () => { credentials: 'same-origin', headers: { 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'XMLHttpRequest' + 'x-requested-with': 'XMLHttpRequest', }, body: new FormData(), }); }); - it('should emit fetchcomplete event', () => new Promise(resolve => { - let form: HTMLFormElement | null = null; + it('should emit fetchcomplete event', () => + new Promise((resolve) => { + let form: HTMLFormElement | null = null; - addOneShotEventListener('fetchcomplete', event => { - expect(event.target).toBe(form); - resolve(); - }); + addOneShotEventListener('fetchcomplete', (event) => { + expect(event.target).toBe(form); + resolve(); + }); - form = submitForm(); - })); + form = submitForm(); + })); it('should reload the page on 300 multiple choices response', () => { - vi.spyOn(global, 'fetch').mockResolvedValue(new Response('', { status: 300})); + vi.spyOn(global, 'fetch').mockResolvedValue(new Response('', { status: 300 })); submitForm(); return waitFor(() => expect(window.location.reload).toHaveBeenCalledTimes(1)); @@ -211,7 +214,7 @@ describe('Remote utilities', () => { describe('Form utilities', () => { beforeEach(() => { - vi.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => { + vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { cb(1); return 1; }); @@ -257,7 +260,7 @@ describe('Form utilities', () => { // jsdom has no implementation for HTMLFormElement.prototype.submit // and will return an error if the event's default isn't prevented - form.addEventListener('submit', event => event.preventDefault()); + form.addEventListener('submit', (event) => event.preventDefault()); const button = document.createElement('button'); button.type = 'submit'; @@ -267,7 +270,7 @@ describe('Form utilities', () => { form.insertAdjacentElement('beforeend', button); document.documentElement.insertAdjacentElement('beforeend', form); - return [ form, button ]; + return [form, button]; }; const submitText = 'Submit'; @@ -276,7 +279,7 @@ describe('Form utilities', () => { const loadingMarkup = 'Loading...'; it('should disable submit button containing a text child on click', () => { - const [ , button ] = createFormAndButton(submitText, loadingText); + const [, button] = createFormAndButton(submitText, loadingText); fireEvent.click(button); expect(button.textContent).toEqual(' Loading...'); @@ -284,7 +287,7 @@ describe('Form utilities', () => { }); it('should disable submit button containing element children on click', () => { - const [ , button ] = createFormAndButton(submitMarkup, loadingMarkup); + const [, button] = createFormAndButton(submitMarkup, loadingMarkup); fireEvent.click(button); expect(button.innerHTML).toEqual(loadingMarkup); @@ -292,7 +295,7 @@ describe('Form utilities', () => { }); it('should not disable anything when the form is invalid', () => { - const [ form, button ] = createFormAndButton(submitText, loadingText); + const [form, button] = createFormAndButton(submitText, loadingText); form.insertAdjacentHTML('afterbegin', ''); fireEvent.click(button); @@ -301,7 +304,7 @@ describe('Form utilities', () => { }); it('should reset submit button containing a text child on completion', () => { - const [ form, button ] = createFormAndButton(submitText, loadingText); + const [form, button] = createFormAndButton(submitText, loadingText); fireEvent.click(button); fireEvent(form, new CustomEvent('reset', { bubbles: true })); @@ -310,7 +313,7 @@ describe('Form utilities', () => { }); it('should reset submit button containing element children on completion', () => { - const [ form, button ] = createFormAndButton(submitMarkup, loadingMarkup); + const [form, button] = createFormAndButton(submitMarkup, loadingMarkup); fireEvent.click(button); fireEvent(form, new CustomEvent('reset', { bubbles: true })); @@ -319,7 +322,7 @@ describe('Form utilities', () => { }); it('should reset disabled form elements on pageshow', () => { - const [ , button ] = createFormAndButton(submitText, loadingText); + const [, button] = createFormAndButton(submitText, loadingText); fireEvent.click(button); fireEvent(window, new CustomEvent('pageshow')); diff --git a/assets/js/__tests__/upload.spec.ts b/assets/js/__tests__/upload.spec.ts index 4f6716660..1003b5223 100644 --- a/assets/js/__tests__/upload.spec.ts +++ b/assets/js/__tests__/upload.spec.ts @@ -29,12 +29,16 @@ describe('Image upload form', () => { let mockPng: File; let mockWebm: File; - beforeAll(async() => { + beforeAll(async () => { const mockPngPath = join(__dirname, 'upload-test.png'); const mockWebmPath = join(__dirname, 'upload-test.webm'); - mockPng = new File([(await promises.readFile(mockPngPath, { encoding: null })).buffer], 'upload-test.png', { type: 'image/png' }); - mockWebm = new File([(await promises.readFile(mockWebmPath, { encoding: null })).buffer], 'upload-test.webm', { type: 'video/webm' }); + mockPng = new File([(await promises.readFile(mockPngPath, { encoding: null })).buffer], 'upload-test.png', { + type: 'image/png', + }); + mockWebm = new File([(await promises.readFile(mockWebmPath, { encoding: null })).buffer], 'upload-test.webm', { + type: 'video/webm', + }); }); beforeAll(() => { @@ -47,7 +51,6 @@ describe('Image upload form', () => { fixEventListeners(window); - let form: HTMLFormElement; let imgPreviews: HTMLDivElement; let fileField: HTMLInputElement; @@ -63,7 +66,9 @@ describe('Image upload form', () => { }; beforeEach(() => { - document.documentElement.insertAdjacentHTML('beforeend', ` + document.documentElement.insertAdjacentHTML( + 'beforeend', + `
@@ -75,7 +80,8 @@ describe('Image upload form', () => {
- `); + `, + ); form = assertNotNull($('form')); imgPreviews = assertNotNull($('#js-image-upload-previews')); @@ -121,7 +127,7 @@ describe('Image upload form', () => { }); }); - it('should block navigation away after an image file is attached, but not after form submission', async() => { + it('should block navigation away after an image file is attached, but not after form submission', async () => { fireEvent.change(fileField, { target: { files: [mockPng] } }); await waitFor(() => { assertFetchButtonIsDisabled(); @@ -131,8 +137,8 @@ describe('Image upload form', () => { const failedUnloadEvent = new Event('beforeunload', { cancelable: true }); expect(fireEvent(window, failedUnloadEvent)).toBe(false); - await new Promise(resolve => { - form.addEventListener('submit', event => { + await new Promise((resolve) => { + form.addEventListener('submit', (event) => { event.preventDefault(); resolve(); }); @@ -143,11 +149,11 @@ describe('Image upload form', () => { expect(fireEvent(window, succeededUnloadEvent)).toBe(true); }); - it('should scrape images when the fetch button is clicked', async() => { + it('should scrape images when the fetch button is clicked', async () => { fetchMock.mockResolvedValue(new Response(JSON.stringify(scrapeResponse), { status: 200 })); fireEvent.input(remoteUrl, { target: { value: 'http://localhost/images/1' } }); - await new Promise(resolve => { + await new Promise((resolve) => { tagsEl.addEventListener('addtag', (event: Event) => { expect((event as CustomEvent).detail).toEqual({ name: 'artist:test' }); resolve(); diff --git a/assets/js/autocomplete.js b/assets/js/autocomplete.js index 1551a6f8d..01ea28936 100644 --- a/assets/js/autocomplete.js +++ b/assets/js/autocomplete.js @@ -10,12 +10,12 @@ import store from './utils/store'; const cache = {}; /** @type {HTMLInputElement} */ let inputField, - /** @type {string} */ - originalTerm, - /** @type {string} */ - originalQuery, - /** @type {TermContext} */ - selectedTerm; + /** @type {string} */ + originalTerm, + /** @type {string} */ + originalQuery, + /** @type {TermContext} */ + selectedTerm; function removeParent() { const parent = document.querySelector('.autocomplete'); @@ -52,15 +52,16 @@ function applySelectedValue(selection) { } function changeSelected(firstOrLast, current, sibling) { - if (current && sibling) { // if the currently selected item has a sibling, move selection to it + if (current && sibling) { + // if the currently selected item has a sibling, move selection to it current.classList.remove('autocomplete__item--selected'); sibling.classList.add('autocomplete__item--selected'); - } - else if (current) { // if the next keypress will take the user outside the list, restore the unautocompleted term + } else if (current) { + // if the next keypress will take the user outside the list, restore the unautocompleted term restoreOriginalValue(); removeSelected(); - } - else if (firstOrLast) { // if no item in the list is selected, select the first or last + } else if (firstOrLast) { + // if no item in the list is selected, select the first or last firstOrLast.classList.add('autocomplete__item--selected'); } } @@ -74,15 +75,16 @@ function isSelectionOutsideCurrentTerm() { function keydownHandler(event) { const selected = document.querySelector('.autocomplete__item--selected'), - firstItem = document.querySelector('.autocomplete__item:first-of-type'), - lastItem = document.querySelector('.autocomplete__item:last-of-type'); + firstItem = document.querySelector('.autocomplete__item:first-of-type'), + lastItem = document.querySelector('.autocomplete__item:last-of-type'); if (isSearchField()) { // Prevent submission of the search field when Enter was hit if (selected && event.keyCode === 13) event.preventDefault(); // Enter // Close autocompletion popup when text cursor is outside current tag - if (selectedTerm && firstItem && (event.keyCode === 37 || event.keyCode === 39)) { // ArrowLeft || ArrowRight + if (selectedTerm && firstItem && (event.keyCode === 37 || event.keyCode === 39)) { + // ArrowLeft || ArrowRight requestAnimationFrame(() => { if (isSelectionOutsideCurrentTerm()) removeParent(); }); @@ -92,7 +94,8 @@ function keydownHandler(event) { if (event.keyCode === 38) changeSelected(lastItem, selected, selected && selected.previousSibling); // ArrowUp if (event.keyCode === 40) changeSelected(firstItem, selected, selected && selected.nextSibling); // ArrowDown if (event.keyCode === 13 || event.keyCode === 27 || event.keyCode === 188) removeParent(); // Enter || Esc || Comma - if (event.keyCode === 38 || event.keyCode === 40) { // ArrowUp || ArrowDown + if (event.keyCode === 38 || event.keyCode === 40) { + // ArrowUp || ArrowDown const newSelected = document.querySelector('.autocomplete__item--selected'); if (newSelected) applySelectedValue(newSelected.dataset.value); event.preventDefault(); @@ -123,8 +126,8 @@ function createItem(list, suggestion) { type: 'click', label: suggestion.label, value: suggestion.value, - } - }) + }, + }), ); }); @@ -133,10 +136,10 @@ function createItem(list, suggestion) { function createList(suggestions) { const parent = document.querySelector('.autocomplete'), - list = document.createElement('ul'); + list = document.createElement('ul'); list.className = 'autocomplete__list'; - suggestions.forEach(suggestion => createItem(list, suggestion)); + suggestions.forEach((suggestion) => createItem(list, suggestion)); parent.appendChild(list); } @@ -173,7 +176,7 @@ function showAutocomplete(suggestions, fetchedTerm, targetInput) { function getSuggestions(term) { // In case source URL was not given at all, do not try sending the request. if (!inputField.dataset.acSource) return []; - return fetch(`${inputField.dataset.acSource}${term}`).then(response => response.json()); + return fetch(`${inputField.dataset.acSource}${term}`).then((response) => response.json()); } function getSelectedTerm() { @@ -193,8 +196,7 @@ function toggleSearchAutocomplete() { for (const searchField of document.querySelectorAll('input[data-ac-mode=search]')) { if (enable) { searchField.autocomplete = 'off'; - } - else { + } else { searchField.removeAttribute('data-ac'); searchField.autocomplete = 'on'; } @@ -210,7 +212,7 @@ function listenAutocomplete() { document.addEventListener('focusin', fetchLocalAutocomplete); - document.addEventListener('input', event => { + document.addEventListener('input', (event) => { removeParent(); fetchLocalAutocomplete(event); window.clearTimeout(timeout); @@ -230,12 +232,13 @@ function listenAutocomplete() { } originalTerm = selectedTerm[1].toLowerCase(); - } - else { + } else { originalTerm = `${inputField.value}`.toLowerCase(); } - const suggestions = localAc.topK(originalTerm, suggestionsCount).map(({ name, imageCount }) => ({ label: `${name} (${imageCount})`, value: name })); + const suggestions = localAc + .topK(originalTerm, suggestionsCount) + .map(({ name, imageCount }) => ({ label: `${name} (${imageCount})`, value: name })); if (suggestions.length) { return showAutocomplete(suggestions, originalTerm, event.target); @@ -248,15 +251,14 @@ function listenAutocomplete() { originalTerm = inputField.value; const fetchedTerm = inputField.value; - const {ac, acMinLength, acSource} = inputField.dataset; + const { ac, acMinLength, acSource } = inputField.dataset; - if (ac && acSource && (fetchedTerm.length >= acMinLength)) { + if (ac && acSource && fetchedTerm.length >= acMinLength) { if (cache[fetchedTerm]) { showAutocomplete(cache[fetchedTerm], fetchedTerm, event.target); - } - else { + } else { // inputField could get overwritten while the suggestions are being fetched - use event.target - getSuggestions(fetchedTerm).then(suggestions => { + getSuggestions(fetchedTerm).then((suggestions) => { if (fetchedTerm === event.target.value) { showAutocomplete(suggestions, fetchedTerm, event.target); } @@ -267,7 +269,7 @@ function listenAutocomplete() { }); // If there's a click outside the inputField, remove autocomplete - document.addEventListener('click', event => { + document.addEventListener('click', (event) => { if (event.target && event.target !== inputField) removeParent(); if (event.target === inputField && isSearchField() && isSelectionOutsideCurrentTerm()) removeParent(); }); @@ -281,8 +283,10 @@ function listenAutocomplete() { fetch(`/autocomplete/compiled?vsn=2&key=${cacheKey}`, { credentials: 'omit', cache: 'force-cache' }) .then(handleError) - .then(resp => resp.arrayBuffer()) - .then(buf => localAc = new LocalAutocompleter(buf)); + .then((resp) => resp.arrayBuffer()) + .then((buf) => { + localAc = new LocalAutocompleter(buf); + }); } } diff --git a/assets/js/booru.js b/assets/js/booru.js index 3e1e7dacf..45f306806 100644 --- a/assets/js/booru.js +++ b/assets/js/booru.js @@ -23,11 +23,11 @@ function persistTag(tagData) { */ function isStale(tag) { const now = new Date().getTime() / 1000; - return tag.fetchedAt === null || tag.fetchedAt < (now - 604800); + return tag.fetchedAt === null || tag.fetchedAt < now - 604800; } function clearTags() { - Object.keys(localStorage).forEach(key => { + Object.keys(localStorage).forEach((key) => { if (key.substring(0, 9) === 'bor_tags_') { store.remove(key); } @@ -40,11 +40,13 @@ function clearTags() { */ function isValidStoredTag(value) { if (value !== null && 'id' in value && 'name' in value && 'images' in value && 'spoiler_image_uri' in value) { - return typeof value.id === 'number' - && typeof value.name === 'string' - && typeof value.images === 'number' - && (value.spoiler_image_uri === null || typeof value.spoiler_image_uri === 'string') - && (value.fetchedAt === null || typeof value.fetchedAt === 'number'); + return ( + typeof value.id === 'number' && + typeof value.name === 'string' && + typeof value.images === 'number' && + (value.spoiler_image_uri === null || typeof value.spoiler_image_uri === 'string') && + (value.fetchedAt === null || typeof value.fetchedAt === 'number') + ); } return false; @@ -82,8 +84,8 @@ function fetchAndPersistTags(tagIds) { const remaining = tagIds.slice(41); fetch(`/fetch/tags?ids[]=${ids.join('&ids[]=')}`) - .then(response => response.json()) - .then(data => data.tags.forEach(tag => persistTag(tag))) + .then((response) => response.json()) + .then((data) => data.tags.forEach((tag) => persistTag(tag))) .then(() => fetchAndPersistTags(remaining)); } @@ -94,7 +96,7 @@ function fetchAndPersistTags(tagIds) { function fetchNewOrStaleTags(tagIds) { const fetchIds = []; - tagIds.forEach(t => { + tagIds.forEach((t) => { const stored = store.get(`bor_tags_${t}`); if (!stored || isStale(stored)) { fetchIds.push(t); @@ -112,17 +114,18 @@ function verifyTagsVersion(latest) { } function initializeFilters() { - const tags = window.booru.spoileredTagList - .concat(window.booru.hiddenTagList) - .filter((a, b, c) => c.indexOf(a) === b); + const tags = window.booru.spoileredTagList.concat(window.booru.hiddenTagList).filter((a, b, c) => c.indexOf(a) === b); verifyTagsVersion(window.booru.tagsVersion); fetchNewOrStaleTags(tags); } function unmarshal(data) { - try { return JSON.parse(data); } - catch { return data; } + try { + return JSON.parse(data); + } catch { + return data; + } } function loadBooruData() { diff --git a/assets/js/boorujs.js b/assets/js/boorujs.js index 6763358fb..6ccfa0e4b 100644 --- a/assets/js/boorujs.js +++ b/assets/js/boorujs.js @@ -11,40 +11,72 @@ import { addTag } from './tagsinput'; // Event types and any qualifying conditions - return true to not run action const types = { - click(event) { return event.button !== 0; /* Left-click only */ }, + click(event) { + return event.button !== 0; /* Left-click only */ + }, - change() { /* No qualifier */ }, + change() { + /* No qualifier */ + }, - fetchcomplete() { /* No qualifier */ }, + fetchcomplete() { + /* No qualifier */ + }, }; const actions = { - hide(data) { selectorCb(data.base, data.value, el => el.classList.add('hidden')); }, + hide(data) { + selectorCb(data.base, data.value, (el) => el.classList.add('hidden')); + }, - tabHide(data) { selectorCbChildren(data.base, data.value, el => el.classList.add('hidden')); }, + tabHide(data) { + selectorCbChildren(data.base, data.value, (el) => el.classList.add('hidden')); + }, - show(data) { selectorCb(data.base, data.value, el => el.classList.remove('hidden')); }, + show(data) { + selectorCb(data.base, data.value, (el) => el.classList.remove('hidden')); + }, - toggle(data) { selectorCb(data.base, data.value, el => el.classList.toggle('hidden')); }, + toggle(data) { + selectorCb(data.base, data.value, (el) => el.classList.toggle('hidden')); + }, - submit(data) { selectorCb(data.base, data.value, el => el.submit()); }, + submit(data) { + selectorCb(data.base, data.value, (el) => el.submit()); + }, - disable(data) { selectorCb(data.base, data.value, el => el.disabled = true); }, + disable(data) { + selectorCb(data.base, data.value, (el) => { + el.disabled = true; + }); + }, copy(data) { document.querySelector(data.value).select(); document.execCommand('copy'); }, - inputvalue(data) { document.querySelector(data.value).value = data.el.dataset.setValue; }, + inputvalue(data) { + document.querySelector(data.value).value = data.el.dataset.setValue; + }, - selectvalue(data) { document.querySelector(data.value).value = data.el.querySelector(':checked').dataset.setValue; }, + selectvalue(data) { + document.querySelector(data.value).value = data.el.querySelector(':checked').dataset.setValue; + }, - checkall(data) { $$(`${data.value} input[type=checkbox]`).forEach(c => { c.checked = !c.checked; }); }, + checkall(data) { + $$(`${data.value} input[type=checkbox]`).forEach((c) => { + c.checked = !c.checked; + }); + }, - focus(data) { document.querySelector(data.value).focus(); }, + focus(data) { + document.querySelector(data.value).focus(); + }, - preventdefault() { /* The existence of this entry is enough */ }, + preventdefault() { + /* The existence of this entry is enough */ + }, addtag(data) { addTag(document.querySelector(data.el.closest('[data-target]').dataset.target), data.el.dataset.tagName); @@ -52,8 +84,8 @@ const actions = { tab(data) { const block = data.el.parentNode.parentNode, - newTab = $(`.block__tab[data-tab="${data.value}"]`), - loadTab = data.el.dataset.loadTab; + newTab = $(`.block__tab[data-tab="${data.value}"]`), + loadTab = data.el.dataset.loadTab; // Switch tab const selectedTab = block.querySelector('.selected'); @@ -70,16 +102,22 @@ const actions = { if (loadTab && !newTab.dataset.loaded) { fetchHtml(loadTab) .then(handleError) - .then(response => response.text()) - .then(response => newTab.innerHTML = response) - .then(() => newTab.dataset.loaded = true) - .catch(() => newTab.textContent = 'Error!'); + .then((response) => response.text()) + .then((response) => { + newTab.innerHTML = response; + }) + .then(() => { + newTab.dataset.loaded = true; + }) + .catch(() => { + newTab.textContent = 'Error!'; + }); } - }, - unfilter(data) { showBlock(data.el.closest('.image-show-container')); }, - + unfilter(data) { + showBlock(data.el.closest('.image-show-container')); + }, }; // Use this function to apply a callback to elements matching the selectors @@ -100,16 +138,14 @@ function selectorCbChildren(base = document, selector, cb) { function matchAttributes(event) { if (!types[event.type](event)) { for (const action in actions) { - const attr = `data-${event.type}-${action.toLowerCase()}`, - el = event.target && event.target.closest(`[${attr}]`), - value = el && el.getAttribute(attr); + el = event.target && event.target.closest(`[${attr}]`), + value = el && el.getAttribute(attr); if (el) { // Return true if you don't want to preventDefault actions[action]({ attr, el, value }) || event.preventDefault(); } - } } } diff --git a/assets/js/burger.ts b/assets/js/burger.ts index d82d032fd..a22b06ad3 100644 --- a/assets/js/burger.ts +++ b/assets/js/burger.ts @@ -40,7 +40,7 @@ function copyUserLinksTo(burger: HTMLElement) { } }; - $$('.js-burger-links').forEach(container => copy(container.children)); + $$('.js-burger-links').forEach((container) => copy(container.children)); } export function setupBurgerMenu() { @@ -53,14 +53,13 @@ export function setupBurgerMenu() { copyUserLinksTo(burger); - toggle.addEventListener('click', event => { + toggle.addEventListener('click', (event) => { event.stopPropagation(); event.preventDefault(); if (content.classList.contains('open')) { close(burger, content, body, root); - } - else { + } else { open(burger, content, body, root); } }); diff --git a/assets/js/captcha.ts b/assets/js/captcha.ts index 44d0d9b6e..5c83cd143 100644 --- a/assets/js/captcha.ts +++ b/assets/js/captcha.ts @@ -5,8 +5,8 @@ import { clearEl, makeEl } from './utils/dom'; function insertCaptcha(_event: Event, target: HTMLInputElement) { const parentElement = assertNotNull(target.parentElement); - const script = makeEl('script', {src: 'https://hcaptcha.com/1/api.js', async: true, defer: true}); - const frame = makeEl('div', {className: 'h-captcha'}); + const script = makeEl('script', { src: 'https://hcaptcha.com/1/api.js', async: true, defer: true }); + const frame = makeEl('div', { className: 'h-captcha' }); frame.dataset.sitekey = target.dataset.sitekey; @@ -17,5 +17,5 @@ function insertCaptcha(_event: Event, target: HTMLInputElement) { } export function bindCaptchaLinks() { - delegate(document, 'click', {'.js-captcha': leftClick(insertCaptcha)}); + delegate(document, 'click', { '.js-captcha': leftClick(insertCaptcha) }); } diff --git a/assets/js/comment.js b/assets/js/comment.js index feb05b59a..b60a6fecb 100644 --- a/assets/js/comment.js +++ b/assets/js/comment.js @@ -18,25 +18,23 @@ function handleError(response) { } function commentPosted(response) { - const commentEditTab = $('#js-comment-form a[data-click-tab="write"]'), - commentEditForm = $('#js-comment-form'), - container = document.getElementById('comments'), - requestOk = response.ok; + const commentEditTab = $('#js-comment-form a[data-click-tab="write"]'), + commentEditForm = $('#js-comment-form'), + container = document.getElementById('comments'), + requestOk = response.ok; commentEditTab.click(); commentEditForm.reset(); if (requestOk) { - response.text().then(text => { + response.text().then((text) => { if (text.includes('
')) { window.location.reload(); - } - else { + } else { displayComments(container, text); } }); - } - else { + } else { window.location.reload(); window.scrollTo(0, 0); // Error message is displayed at the top of the page (flash) } @@ -44,10 +42,10 @@ function commentPosted(response) { function loadParentPost(event) { const clickedLink = event.target, - // Find the comment containing the link that was clicked - fullComment = clickedLink.closest('article.block'), - // Look for a potential image and comment ID - commentMatches = /(\w+)#comment_(\w+)$/.exec(clickedLink.getAttribute('href')); + // Find the comment containing the link that was clicked + fullComment = clickedLink.closest('article.block'), + // Look for a potential image and comment ID + commentMatches = /(\w+)#comment_(\w+)$/.exec(clickedLink.getAttribute('href')); // If the clicked link is already active, just clear the parent comments if (clickedLink.classList.contains('active_reply_link')) { @@ -58,11 +56,11 @@ function loadParentPost(event) { if (commentMatches) { // If the regex matched, get the image and comment ID - const [ , imageId, commentId ] = commentMatches; + const [, imageId, commentId] = commentMatches; fetchHtml(`/images/${imageId}/comments/${commentId}`) .then(handleError) - .then(data => { + .then((data) => { clearParentPost(clickedLink, fullComment); insertParentPost(data, clickedLink, fullComment); }); @@ -99,7 +97,7 @@ function clearParentPost(clickedLink, fullComment) { } // Remove class active_reply_link from all links in the comment - [].slice.call(fullComment.getElementsByClassName('active_reply_link')).forEach(link => { + [].slice.call(fullComment.getElementsByClassName('active_reply_link')).forEach((link) => { link.classList.remove('active_reply_link'); }); @@ -121,14 +119,17 @@ function displayComments(container, commentsHtml) { function loadComments(event) { const container = document.getElementById('comments'), - hasHref = event.target && event.target.getAttribute('href'), - hasHash = window.location.hash && window.location.hash.match(/#comment_([a-f0-9]+)/), - getURL = hasHref || (hasHash ? `${container.dataset.currentUrl}?comment_id=${window.location.hash.substring(9, window.location.hash.length)}` - : container.dataset.currentUrl); + hasHref = event.target && event.target.getAttribute('href'), + hasHash = window.location.hash && window.location.hash.match(/#comment_([a-f0-9]+)/), + getURL = + hasHref || + (hasHash + ? `${container.dataset.currentUrl}?comment_id=${window.location.hash.substring(9, window.location.hash.length)}` + : container.dataset.currentUrl); fetchHtml(getURL) .then(handleError) - .then(data => { + .then((data) => { displayComments(container, data); // Make sure the :target CSS selector applies to the inserted content @@ -143,17 +144,16 @@ function loadComments(event) { } function setupComments() { - const comments = document.getElementById('comments'), - hasHash = window.location.hash && window.location.hash.match(/^#comment_([a-f0-9]+)$/), - targetOnPage = hasHash ? Boolean($(window.location.hash)) : true; + const comments = document.getElementById('comments'), + hasHash = window.location.hash && window.location.hash.match(/^#comment_([a-f0-9]+)$/), + targetOnPage = hasHash ? Boolean($(window.location.hash)) : true; // Load comments over AJAX if we are on a page with element #comments if (comments) { if (!comments.dataset.loaded || !targetOnPage) { // There is no event associated with the initial load, so use false loadComments(false); - } - else { + } else { filterNode(comments); } } @@ -165,8 +165,9 @@ function setupComments() { '#js-refresh-comments': loadComments, }; - document.addEventListener('click', event => { - if (event.button === 0) { // Left-click only + document.addEventListener('click', (event) => { + if (event.button === 0) { + // Left-click only for (const target in targets) { if (event.target && event.target.closest(target)) { targets[target](event) && event.preventDefault(); @@ -175,7 +176,7 @@ function setupComments() { } }); - document.addEventListener('fetchcomplete', event => { + document.addEventListener('fetchcomplete', (event) => { if (event.target.id === 'js-comment-form') commentPosted(event.detail); }); } diff --git a/assets/js/duplicate_reports.ts b/assets/js/duplicate_reports.ts index 55cdfeb16..623aaf8b0 100644 --- a/assets/js/duplicate_reports.ts +++ b/assets/js/duplicate_reports.ts @@ -15,7 +15,7 @@ export function setupDupeReports() { } function setupSwipe(swipe: SVGSVGElement) { - const [ clip, divider ] = $$('#clip rect, #divider', swipe); + const [clip, divider] = $$('#clip rect, #divider', swipe); const { width } = swipe.viewBox.baseVal; function moveDivider({ clientX }: MouseEvent) { diff --git a/assets/js/fp.ts b/assets/js/fp.ts index 65f0c5837..25fc7bd90 100644 --- a/assets/js/fp.ts +++ b/assets/js/fp.ts @@ -11,19 +11,19 @@ const storageKey = 'cached_ses_value'; declare global { interface Keyboard { - getLayoutMap: () => Promise> + getLayoutMap: () => Promise>; } interface UserAgentData { - brands: [{brand: string, version: string}], - mobile: boolean, - platform: string, + brands: [{ brand: string; version: string }]; + mobile: boolean; + platform: string; } interface Navigator { - deviceMemory: number | undefined, - keyboard: Keyboard | undefined, - userAgentData: UserAgentData | undefined, + deviceMemory: number | undefined; + keyboard: Keyboard | undefined; + userAgentData: UserAgentData | undefined; } } @@ -45,10 +45,10 @@ function cyrb53(str: string, seed: number = 0x16fe7b0a): number { h2 = Math.imul(h2 ^ ch, 1597334677); } - h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507); - h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909); - h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507); - h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909); + h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507); + h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909); + h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507); + h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909); return 4294967296 * (2097151 & h2) + (h1 >>> 0); } @@ -102,8 +102,8 @@ function getUserAgentBrands(): string { // NB: Chromium implements GREASE protocol to prevent ossification of // the "Not a brand" string - see https://stackoverflow.com/a/64443187 brands = data.brands - .filter(e => !e.brand.match(/.*ot.*rand.*/gi)) - .map(e => `${e.brand}${e.version}`) + .filter((e) => !e.brand.match(/.*ot.*rand.*/gi)) + .map((e) => `${e.brand}${e.version}`) .sort() .join(''); } @@ -161,9 +161,7 @@ async function createFp(): Promise { new Date().getTimezoneOffset().toString(), ]; - return cyrb53(prints.join('')) - .toString(16) - .padStart(14, '0'); + return cyrb53(prints.join('')).toString(16).padStart(14, '0'); } /** diff --git a/assets/js/galleries.ts b/assets/js/galleries.ts index 386bb2780..73f0a0b6a 100644 --- a/assets/js/galleries.ts +++ b/assets/js/galleries.ts @@ -11,7 +11,7 @@ import { fetchJson } from './utils/requests'; export function setupGalleryEditing() { if (!$('.rearrange-button')) return; - const [ rearrangeEl, saveEl ] = $$('.rearrange-button'); + const [rearrangeEl, saveEl] = $$('.rearrange-button'); const sortableEl = assertNotNull($('#sortable')); const containerEl = assertNotNull($('.js-resizable-media-container')); @@ -22,7 +22,9 @@ export function setupGalleryEditing() { initDraggables(); - $$('.media-box', containerEl).forEach(i => i.draggable = true); + $$('.media-box', containerEl).forEach((i) => { + i.draggable = true; + }); rearrangeEl.addEventListener('click', () => { sortableEl.classList.add('editing'); @@ -33,8 +35,9 @@ export function setupGalleryEditing() { sortableEl.classList.remove('editing'); containerEl.classList.remove('drag-container'); - newImages = $$('.image-container', containerEl) - .map(i => parseInt(assertNotUndefined(i.dataset.imageId), 10)); + newImages = $$('.image-container', containerEl).map((i) => + parseInt(assertNotUndefined(i.dataset.imageId), 10), + ); // If nothing changed, don't bother. if (arraysEqual(newImages, oldImages)) return; @@ -43,7 +46,9 @@ export function setupGalleryEditing() { fetchJson('PATCH', reorderPath, { image_ids: newImages, - // copy the array again so that we have the newly updated set - }).then(() => oldImages = newImages.slice()); + // copy the array again so that we have the newly updated set + }).then(() => { + oldImages = newImages.slice(); + }); }); } diff --git a/assets/js/graph.ts b/assets/js/graph.ts index 5014600c0..035e33ed7 100644 --- a/assets/js/graph.ts +++ b/assets/js/graph.ts @@ -22,7 +22,7 @@ function graphSlice(el: SVGSVGElement, width: number, offset: number) { } function resizeGraphs() { - $$('#js-graph-svg').forEach(el => { + $$('#js-graph-svg').forEach((el) => { const parent: HTMLElement | null = el.parentElement; if (parent) { @@ -34,7 +34,7 @@ function resizeGraphs() { function scaleGraph(target: HTMLElement, min: number, max: number) { const targetSvg = $('#js-graph-svg', target); - if (!targetSvg) { return; } + if (!targetSvg) return; const cw = target.clientWidth; const diff = 100 - (max - min); @@ -47,14 +47,14 @@ function scaleGraph(target: HTMLElement, min: number, max: number) { } function setupSliders() { - $$('#js-graph-slider').forEach(el => { + $$('#js-graph-slider').forEach((el) => { const targetId = el.getAttribute('data-target'); - if (!targetId) { return; } + if (!targetId) return; const target = $(targetId); - if (!target) { return; } + if (!target) return; el.addEventListener('input', () => { const min = Number(el.getAttribute('valuemin') || '0'); diff --git a/assets/js/image_expansion.js b/assets/js/image_expansion.js index 7bd6cb26e..78d90ee31 100644 --- a/assets/js/image_expansion.js +++ b/assets/js/image_expansion.js @@ -5,7 +5,7 @@ const imageVersions = { // [width, height] small: [320, 240], medium: [800, 600], - large: [1280, 1024] + large: [1280, 1024], }; /** @@ -14,7 +14,7 @@ const imageVersions = { */ function selectVersion(imageWidth, imageHeight, imageSize, imageMime) { let viewWidth = document.documentElement.clientWidth, - viewHeight = document.documentElement.clientHeight; + viewHeight = document.documentElement.clientHeight; // load hires if that's what you asked for if (store.get('serve_hidpi')) { @@ -31,9 +31,9 @@ function selectVersion(imageWidth, imageHeight, imageSize, imageMime) { // .find() is not supported in older browsers, using a loop for (let i = 0, versions = Object.keys(imageVersions); i < versions.length; ++i) { const version = versions[i], - dimensions = imageVersions[version], - versionWidth = Math.min(imageWidth, dimensions[0]), - versionHeight = Math.min(imageHeight, dimensions[1]); + dimensions = imageVersions[version], + versionWidth = Math.min(imageWidth, dimensions[0]), + versionHeight = Math.min(imageHeight, dimensions[1]); if (versionWidth > viewWidth || versionHeight > viewHeight) { return version; } @@ -57,11 +57,11 @@ function selectVersion(imageWidth, imageHeight, imageSize, imageMime) { */ function pickAndResize(elem) { const imageWidth = parseInt(elem.dataset.width, 10), - imageHeight = parseInt(elem.dataset.height, 10), - imageSize = parseInt(elem.dataset.imageSize, 10), - imageMime = elem.dataset.mimeType, - scaled = elem.dataset.scaled, - uris = JSON.parse(elem.dataset.uris); + imageHeight = parseInt(elem.dataset.height, 10), + imageSize = parseInt(elem.dataset.imageSize, 10), + imageMime = elem.dataset.mimeType, + scaled = elem.dataset.scaled, + uris = JSON.parse(elem.dataset.uris); let version = 'full'; @@ -91,7 +91,8 @@ function pickAndResize(elem) { if (imageFormat === 'mp4') { elem.classList.add('full-height'); - elem.insertAdjacentHTML('afterbegin', + elem.insertAdjacentHTML( + 'afterbegin', `` + `, ); - } - else if (imageFormat === 'webm') { - elem.insertAdjacentHTML('afterbegin', + } else if (imageFormat === 'webm') { + elem.insertAdjacentHTML( + 'afterbegin', `` + `, ); const video = elem.querySelector('video'); if (scaled === 'true') { video.className = 'image-scaled'; - } - else if (scaled === 'partscaled') { + } else if (scaled === 'partscaled') { video.className = 'image-partscaled'; } - } - else { + } else { let image; if (scaled === 'true') { image = ``; - } - else if (scaled === 'partscaled') { + } else if (scaled === 'partscaled') { image = ``; - } - else { + } else { image = ``; } if (elem.innerHTML === image) return; @@ -148,11 +145,9 @@ function bindImageForClick(target) { target.addEventListener('click', () => { if (target.getAttribute('data-scaled') === 'true') { target.setAttribute('data-scaled', 'partscaled'); - } - else if (target.getAttribute('data-scaled') === 'partscaled') { + } else if (target.getAttribute('data-scaled') === 'partscaled') { target.setAttribute('data-scaled', 'false'); - } - else { + } else { target.setAttribute('data-scaled', 'true'); } @@ -161,7 +156,7 @@ function bindImageForClick(target) { } function bindImageTarget(node = document) { - $$('.image-target', node).forEach(target => { + $$('.image-target', node).forEach((target) => { pickAndResize(target); if (target.dataset.mimeType === 'video/webm') { diff --git a/assets/js/imagesclientside.ts b/assets/js/imagesclientside.ts index add108c98..2d6d81414 100644 --- a/assets/js/imagesclientside.ts +++ b/assets/js/imagesclientside.ts @@ -12,12 +12,7 @@ import { AstMatcher } from './query/types'; type CallbackType = 'tags' | 'complex'; type RunCallback = (img: HTMLDivElement, tags: TagData[], type: CallbackType) => void; -function run( - img: HTMLDivElement, - tags: TagData[], - complex: AstMatcher, - runCallback: RunCallback -): boolean { +function run(img: HTMLDivElement, tags: TagData[], complex: AstMatcher, runCallback: RunCallback): boolean { const hit = (() => { // Check tags array first to provide more precise filter explanations const hitTags = imageHitsTags(img, tags); @@ -56,49 +51,48 @@ function bannerImage(tagsHit: TagData[]) { // TODO: this approach is not suitable for translations because it depends on // markup embedded in the page adjacent to this text -/* eslint-disable indent */ - function hideThumbTyped(img: HTMLDivElement, tagsHit: TagData[], type: CallbackType) { - const bannerText = type === 'tags' ? `[HIDDEN] ${displayTags(tagsHit)}` - : '[HIDDEN] (Complex Filter)'; + const bannerText = type === 'tags' ? `[HIDDEN] ${displayTags(tagsHit)}` : '[HIDDEN] (Complex Filter)'; hideThumb(img, bannerImage(tagsHit), bannerText); } function spoilerThumbTyped(img: HTMLDivElement, tagsHit: TagData[], type: CallbackType) { - const bannerText = type === 'tags' ? displayTags(tagsHit) - : '(Complex Filter)'; + const bannerText = type === 'tags' ? displayTags(tagsHit) : '(Complex Filter)'; spoilerThumb(img, bannerImage(tagsHit), bannerText); } function hideBlockTyped(img: HTMLDivElement, tagsHit: TagData[], type: CallbackType) { - const bannerText = type === 'tags' ? `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is hidden by ` - : 'This image was hidden by a complex tag expression in '; + const bannerText = + type === 'tags' + ? `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is hidden by ` + : 'This image was hidden by a complex tag expression in '; spoilerBlock(img, bannerImage(tagsHit), bannerText); } function spoilerBlockTyped(img: HTMLDivElement, tagsHit: TagData[], type: CallbackType) { - const bannerText = type === 'tags' ? `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is spoilered by ` - : 'This image was spoilered by a complex tag expression in '; + const bannerText = + type === 'tags' + ? `This image is tagged ${escapeHtml(tagsHit[0].name)}, which is spoilered by ` + : 'This image was spoilered by a complex tag expression in '; spoilerBlock(img, bannerImage(tagsHit), bannerText); } -/* eslint-enable indent */ - export function filterNode(node: Pick) { - const hiddenTags = getHiddenTags(), spoileredTags = getSpoileredTags(); + const hiddenTags = getHiddenTags(), + spoileredTags = getSpoileredTags(); const { hiddenFilter, spoileredFilter } = window.booru; // Image thumb boxes with vote and fave buttons on them $$('.image-container', node) - .filter(img => !run(img, hiddenTags, hiddenFilter, hideThumbTyped)) - .filter(img => !run(img, spoileredTags, spoileredFilter, spoilerThumbTyped)) - .forEach(img => showThumb(img)); + .filter((img) => !run(img, hiddenTags, hiddenFilter, hideThumbTyped)) + .filter((img) => !run(img, spoileredTags, spoileredFilter, spoilerThumbTyped)) + .forEach((img) => showThumb(img)); // Individual image pages and images in posts/comments $$('.image-show-container', node) - .filter(img => !run(img, hiddenTags, hiddenFilter, hideBlockTyped)) - .filter(img => !run(img, spoileredTags, spoileredFilter, spoilerBlockTyped)) - .forEach(img => showBlock(img)); + .filter((img) => !run(img, hiddenTags, hiddenFilter, hideBlockTyped)) + .filter((img) => !run(img, spoileredTags, spoileredFilter, spoilerBlockTyped)) + .forEach((img) => showBlock(img)); } export function initImagesClientside() { diff --git a/assets/js/input-duplicator.ts b/assets/js/input-duplicator.ts index e82c892d7..6ce8a5028 100644 --- a/assets/js/input-duplicator.ts +++ b/assets/js/input-duplicator.ts @@ -13,7 +13,7 @@ export function inputDuplicatorCreator({ addButtonSelector, fieldSelector, maxInputCountSelector, - removeButtonSelector + removeButtonSelector, }: InputDuplicatorOptions) { const addButton = $(addButtonSelector); if (!addButton) { @@ -35,14 +35,13 @@ export function inputDuplicatorCreator({ }; delegate(form, 'click', { - [removeButtonSelector]: leftClick(fieldRemover) + [removeButtonSelector]: leftClick(fieldRemover), }); - const maxOptionCountElement = assertNotNull($(maxInputCountSelector, form)); const maxOptionCount = parseInt(maxOptionCountElement.innerHTML, 10); - addButton.addEventListener('click', e => { + addButton.addEventListener('click', (e) => { e.preventDefault(); const existingFields = $$(fieldSelector, form); @@ -53,7 +52,7 @@ export function inputDuplicatorCreator({ const prevField = existingFields[existingFieldsLength - 1]; const prevFieldCopy = prevField.cloneNode(true) as HTMLElement; - $$('input', prevFieldCopy).forEach(prevFieldCopyInput => { + $$('input', prevFieldCopy).forEach((prevFieldCopyInput) => { // Reset new input's value prevFieldCopyInput.value = ''; prevFieldCopyInput.removeAttribute('value'); diff --git a/assets/js/interactions.js b/assets/js/interactions.js index 95d3bad0b..fac8b5e92 100644 --- a/assets/js/interactions.js +++ b/assets/js/interactions.js @@ -6,18 +6,22 @@ import { fetchJson } from './utils/requests'; import { $ } from './utils/dom'; const endpoints = { - vote(imageId) { return `/images/${imageId}/vote`; }, - fave(imageId) { return `/images/${imageId}/fave`; }, - hide(imageId) { return `/images/${imageId}/hide`; }, + vote(imageId) { + return `/images/${imageId}/vote`; + }, + fave(imageId) { + return `/images/${imageId}/fave`; + }, + hide(imageId) { + return `/images/${imageId}/hide`; + }, }; -const spoilerDownvoteMsg = - 'Neigh! - Remove spoilered tags from your filters to downvote from thumbnails'; +const spoilerDownvoteMsg = 'Neigh! - Remove spoilered tags from your filters to downvote from thumbnails'; /* Quick helper function to less verbosely iterate a QSA */ function onImage(id, selector, cb) { - [].forEach.call( - document.querySelectorAll(`${selector}[data-image-id="${id}"]`), cb); + [].forEach.call(document.querySelectorAll(`${selector}[data-image-id="${id}"]`), cb); } /* Since JS modifications to webpages, except form inputs, are not stored @@ -35,28 +39,35 @@ function modifyCache(callback) { } function cacheStatus(imageId, interactionType, value) { - modifyCache(cache => { + modifyCache((cache) => { cache[`${imageId}${interactionType}`] = { imageId, interactionType, value }; return cache; }); } function uncacheStatus(imageId, interactionType) { - modifyCache(cache => { + modifyCache((cache) => { delete cache[`${imageId}${interactionType}`]; return cache; }); } function setScore(imageId, data) { - onImage(imageId, '.score', - el => el.textContent = data.score); - onImage(imageId, '.favorites', - el => el.textContent = data.faves); - onImage(imageId, '.upvotes', - el => el.textContent = data.upvotes); - onImage(imageId, '.downvotes', - el => el.textContent = data.downvotes); + onImage(imageId, '.score', (el) => { + el.textContent = data.score; + }); + + onImage(imageId, '.favorites', (el) => { + el.textContent = data.faves; + }); + + onImage(imageId, '.upvotes', (el) => { + el.textContent = data.upvotes; + }); + + onImage(imageId, '.downvotes', (el) => { + el.textContent = data.downvotes; + }); } /* These change the visual appearance of interaction links. @@ -64,58 +75,50 @@ function setScore(imageId, data) { function showUpvoted(imageId) { cacheStatus(imageId, 'voted', 'up'); - onImage(imageId, '.interaction--upvote', - el => el.classList.add('active')); + onImage(imageId, '.interaction--upvote', (el) => el.classList.add('active')); } function showDownvoted(imageId) { cacheStatus(imageId, 'voted', 'down'); - onImage(imageId, '.interaction--downvote', - el => el.classList.add('active')); + onImage(imageId, '.interaction--downvote', (el) => el.classList.add('active')); } function showFaved(imageId) { cacheStatus(imageId, 'faved', ''); - onImage(imageId, '.interaction--fave', - el => el.classList.add('active')); + onImage(imageId, '.interaction--fave', (el) => el.classList.add('active')); } function showHidden(imageId) { cacheStatus(imageId, 'hidden', ''); - onImage(imageId, '.interaction--hide', - el => el.classList.add('active')); + onImage(imageId, '.interaction--hide', (el) => el.classList.add('active')); } function resetVoted(imageId) { uncacheStatus(imageId, 'voted'); - onImage(imageId, '.interaction--upvote', - el => el.classList.remove('active')); + onImage(imageId, '.interaction--upvote', (el) => el.classList.remove('active')); - onImage(imageId, '.interaction--downvote', - el => el.classList.remove('active')); + onImage(imageId, '.interaction--downvote', (el) => el.classList.remove('active')); } function resetFaved(imageId) { uncacheStatus(imageId, 'faved'); - onImage(imageId, '.interaction--fave', - el => el.classList.remove('active')); + onImage(imageId, '.interaction--fave', (el) => el.classList.remove('active')); } function resetHidden(imageId) { uncacheStatus(imageId, 'hidden'); - onImage(imageId, '.interaction--hide', - el => el.classList.remove('active')); + onImage(imageId, '.interaction--hide', (el) => el.classList.remove('active')); } function interact(type, imageId, method, data = {}) { return fetchJson(method, endpoints[type](imageId), data) - .then(res => res.json()) - .then(res => setScore(imageId, res)); + .then((res) => res.json()) + .then((res) => setScore(imageId, res)); } function displayInteractionSet(interactions) { - interactions.forEach(i => { + interactions.forEach((i) => { switch (i.interaction_type) { case 'faved': showFaved(i.image_id); @@ -131,7 +134,6 @@ function displayInteractionSet(interactions) { } function loadInteractions() { - /* Set up the actual interactions */ displayInteractionSet(window.booru.interactions); displayInteractionSet(getCache()); @@ -141,68 +143,71 @@ function loadInteractions() { if (!document.getElementById('imagelist-container')) return; /* Users will blind downvote without this */ - window.booru.imagesWithDownvotingDisabled.forEach(i => { - onImage(i, '.interaction--downvote', a => { - + window.booru.imagesWithDownvotingDisabled.forEach((i) => { + onImage(i, '.interaction--downvote', (a) => { // TODO Use a 'js-' class to target these instead const icon = a.querySelector('i') || a.querySelector('.oc-icon-small'); icon.setAttribute('title', spoilerDownvoteMsg); a.classList.add('disabled'); - a.addEventListener('click', event => { - event.stopPropagation(); - event.preventDefault(); - }, true); - + a.addEventListener( + 'click', + (event) => { + event.stopPropagation(); + event.preventDefault(); + }, + true, + ); }); }); - } const targets = { - /* Active-state targets first */ '.interaction--upvote.active'(imageId) { - interact('vote', imageId, 'DELETE') - .then(() => resetVoted(imageId)); + interact('vote', imageId, 'DELETE').then(() => resetVoted(imageId)); }, '.interaction--downvote.active'(imageId) { - interact('vote', imageId, 'DELETE') - .then(() => resetVoted(imageId)); + interact('vote', imageId, 'DELETE').then(() => resetVoted(imageId)); }, '.interaction--fave.active'(imageId) { - interact('fave', imageId, 'DELETE') - .then(() => resetFaved(imageId)); + interact('fave', imageId, 'DELETE').then(() => resetFaved(imageId)); }, '.interaction--hide.active'(imageId) { - interact('hide', imageId, 'DELETE') - .then(() => resetHidden(imageId)); + interact('hide', imageId, 'DELETE').then(() => resetHidden(imageId)); }, /* Inactive targets */ '.interaction--upvote:not(.active)'(imageId) { - interact('vote', imageId, 'POST', { up: true }) - .then(() => { resetVoted(imageId); showUpvoted(imageId); }); + interact('vote', imageId, 'POST', { up: true }).then(() => { + resetVoted(imageId); + showUpvoted(imageId); + }); }, '.interaction--downvote:not(.active)'(imageId) { - interact('vote', imageId, 'POST', { up: false }) - .then(() => { resetVoted(imageId); showDownvoted(imageId); }); + interact('vote', imageId, 'POST', { up: false }).then(() => { + resetVoted(imageId); + showDownvoted(imageId); + }); }, '.interaction--fave:not(.active)'(imageId) { - interact('fave', imageId, 'POST') - .then(() => { resetVoted(imageId); showFaved(imageId); showUpvoted(imageId); }); + interact('fave', imageId, 'POST').then(() => { + resetVoted(imageId); + showFaved(imageId); + showUpvoted(imageId); + }); }, '.interaction--hide:not(.active)'(imageId) { - interact('hide', imageId, 'POST') - .then(() => { showHidden(imageId); }); + interact('hide', imageId, 'POST').then(() => { + showHidden(imageId); + }); }, - }; function bindInteractions() { - document.addEventListener('click', event => { - - if (event.button === 0) { // Is it a left-click? + document.addEventListener('click', (event) => { + if (event.button === 0) { + // Is it a left-click? for (const target in targets) { /* Event delegation doesn't quite grab what we want here. */ const link = event.target && event.target.closest(target); @@ -213,21 +218,20 @@ function bindInteractions() { } } } - }); } function loggedOutInteractions() { - [].forEach.call(document.querySelectorAll('.interaction--fave,.interaction--upvote,.interaction--downvote'), - a => a.setAttribute('href', '/sessions/new')); + [].forEach.call(document.querySelectorAll('.interaction--fave,.interaction--upvote,.interaction--downvote'), (a) => + a.setAttribute('href', '/sessions/new'), + ); } function setupInteractions() { if (window.booru.userIsSignedIn) { bindInteractions(); loadInteractions(); - } - else { + } else { loggedOutInteractions(); } } diff --git a/assets/js/markdowntoolbar.js b/assets/js/markdowntoolbar.js index 0c8c7d8a4..93ce1af9a 100644 --- a/assets/js/markdowntoolbar.js +++ b/assets/js/markdowntoolbar.js @@ -7,19 +7,19 @@ import { $, $$ } from './utils/dom'; const markdownSyntax = { bold: { action: wrapSelection, - options: { prefix: '**', shortcutKey: 'b' } + options: { prefix: '**', shortcutKey: 'b' }, }, italics: { action: wrapSelection, - options: { prefix: '*', shortcutKey: 'i' } + options: { prefix: '*', shortcutKey: 'i' }, }, under: { action: wrapSelection, - options: { prefix: '__', shortcutKey: 'u' } + options: { prefix: '__', shortcutKey: 'u' }, }, spoiler: { action: wrapSelection, - options: { prefix: '||', shortcutKey: 's' } + options: { prefix: '||', shortcutKey: 's' }, }, code: { action: wrapSelectionOrLines, @@ -29,57 +29,56 @@ const markdownSyntax = { prefixMultiline: '```\n', suffixMultiline: '\n```', singleWrap: true, - shortcutKey: 'e' - } + shortcutKey: 'e', + }, }, strike: { action: wrapSelection, - options: { prefix: '~~' } + options: { prefix: '~~' }, }, superscript: { action: wrapSelection, - options: { prefix: '^' } + options: { prefix: '^' }, }, subscript: { action: wrapSelection, - options: { prefix: '%' } + options: { prefix: '%' }, }, quote: { action: wrapLines, - options: { prefix: '> ' } + options: { prefix: '> ' }, }, link: { action: insertLink, - options: { shortcutKey: 'l' } + options: { shortcutKey: 'l' }, }, image: { action: insertLink, - options: { image: true, shortcutKey: 'k' } + options: { image: true, shortcutKey: 'k' }, }, escape: { action: escapeSelection, - options: { escapeChar: '\\' } - } + options: { escapeChar: '\\' }, + }, }; function getSelections(textarea, linesOnly = false) { let { selectionStart, selectionEnd } = textarea, - selection = textarea.value.substring(selectionStart, selectionEnd), - leadingSpace = '', - trailingSpace = '', - caret; + selection = textarea.value.substring(selectionStart, selectionEnd), + leadingSpace = '', + trailingSpace = '', + caret; const processLinesOnly = linesOnly instanceof RegExp ? linesOnly.test(selection) : linesOnly; if (processLinesOnly) { const explorer = /\n/g; let startNewlineIndex = 0, - endNewlineIndex = textarea.value.length; + endNewlineIndex = textarea.value.length; while (explorer.exec(textarea.value)) { const { lastIndex } = explorer; if (lastIndex <= selectionStart) { startNewlineIndex = lastIndex; - } - else if (lastIndex > selectionEnd) { + } else if (lastIndex > selectionEnd) { endNewlineIndex = lastIndex - 1; break; } @@ -96,8 +95,7 @@ function getSelections(textarea, linesOnly = false) { } selectionEnd = endNewlineIndex; selection = textarea.value.substring(selectionStart, selectionEnd); - } - else { + } else { // Deselect trailing space and line break for (caret = selection.length - 1; caret > 0; caret--) { if (selection[caret] !== ' ' && selection[caret] !== '\n') break; @@ -117,22 +115,23 @@ function getSelections(textarea, linesOnly = false) { processLinesOnly, selectedText: selection, beforeSelection: textarea.value.substring(0, selectionStart) + leadingSpace, - afterSelection: trailingSpace + textarea.value.substring(selectionEnd) + afterSelection: trailingSpace + textarea.value.substring(selectionEnd), }; } function transformSelection(textarea, transformer, eachLine) { const { selectedText, beforeSelection, afterSelection, processLinesOnly } = getSelections(textarea, eachLine), - // For long comments, record scrollbar position to restore it later - { scrollTop } = textarea; + // For long comments, record scrollbar position to restore it later + { scrollTop } = textarea; const { newText, caretOffset } = transformer(selectedText, processLinesOnly); textarea.value = beforeSelection + newText + afterSelection; - const newSelectionStart = caretOffset >= 1 - ? beforeSelection.length + caretOffset - : textarea.value.length - afterSelection.length - caretOffset; + const newSelectionStart = + caretOffset >= 1 + ? beforeSelection.length + caretOffset + : textarea.value.length - afterSelection.length - caretOffset; textarea.selectionStart = newSelectionStart; textarea.selectionEnd = newSelectionStart; @@ -151,19 +150,19 @@ function insertLink(textarea, options) { } const prefix = options.image ? '![' : '[', - suffix = `](${hyperlink})`; + suffix = `](${hyperlink})`; wrapSelection(textarea, { prefix, suffix }); } function wrapSelection(textarea, options) { - transformSelection(textarea, selectedText => { + transformSelection(textarea, (selectedText) => { const { text = selectedText, prefix = '', suffix = options.prefix } = options, - emptyText = text === ''; + emptyText = text === ''; let newText = text; if (!emptyText) { - newText = text.replace(/(\n{2,})/g, match => { + newText = text.replace(/(\n{2,})/g, (match) => { return suffix + match + prefix; }); } @@ -172,26 +171,33 @@ function wrapSelection(textarea, options) { return { newText, - caretOffset: emptyText ? prefix.length : newText.length + caretOffset: emptyText ? prefix.length : newText.length, }; }); } function wrapLines(textarea, options, eachLine = true) { - transformSelection(textarea, (selectedText, processLinesOnly) => { - const { text = selectedText, singleWrap = false } = options, - prefix = (processLinesOnly && options.prefixMultiline) || options.prefix || '', - suffix = (processLinesOnly && options.suffixMultiline) || options.suffix || '', - emptyText = text === ''; - let newText = singleWrap - ? prefix + text.trim() + suffix - : text.split(/\n/g).map(line => prefix + line.trim() + suffix).join('\n'); - - // Force a space at the end of lines with only blockquote markers - newText = newText.replace(/^((?:>\s+)*)>$/gm, '$1> '); - - return { newText, caretOffset: emptyText ? prefix.length : newText.length }; - }, eachLine); + transformSelection( + textarea, + (selectedText, processLinesOnly) => { + const { text = selectedText, singleWrap = false } = options, + prefix = (processLinesOnly && options.prefixMultiline) || options.prefix || '', + suffix = (processLinesOnly && options.suffixMultiline) || options.suffix || '', + emptyText = text === ''; + let newText = singleWrap + ? prefix + text.trim() + suffix + : text + .split(/\n/g) + .map((line) => prefix + line.trim() + suffix) + .join('\n'); + + // Force a space at the end of lines with only blockquote markers + newText = newText.replace(/^((?:>\s+)*)>$/gm, '$1> '); + + return { newText, caretOffset: emptyText ? prefix.length : newText.length }; + }, + eachLine, + ); } function wrapSelectionOrLines(textarea, options) { @@ -199,9 +205,9 @@ function wrapSelectionOrLines(textarea, options) { } function escapeSelection(textarea, options) { - transformSelection(textarea, selectedText => { + transformSelection(textarea, (selectedText) => { const { text = selectedText } = options, - emptyText = text === ''; + emptyText = text === ''; if (emptyText) return; @@ -209,7 +215,7 @@ function escapeSelection(textarea, options) { return { newText, - caretOffset: newText.length + caretOffset: newText.length, }; }); } @@ -218,20 +224,27 @@ function clickHandler(event) { const button = event.target.closest('.communication__toolbar__button'); if (!button) return; const toolbar = button.closest('.communication__toolbar'), - // There may be multiple toolbars present on the page, - // in the case of image pages with description edit active - // we target the textarea that shares the same parent as the toolbar - textarea = $('.js-toolbar-input', toolbar.parentNode), - id = button.dataset.syntaxId; + // There may be multiple toolbars present on the page, + // in the case of image pages with description edit active + // we target the textarea that shares the same parent as the toolbar + textarea = $('.js-toolbar-input', toolbar.parentNode), + id = button.dataset.syntaxId; markdownSyntax[id].action(textarea, markdownSyntax[id].options); textarea.focus(); } function shortcutHandler(event) { - if (!event.ctrlKey || (window.navigator.platform === 'MacIntel' && !event.metaKey) || event.shiftKey || event.altKey) return; + if ( + !event.ctrlKey || + (window.navigator.platform === 'MacIntel' && !event.metaKey) || + event.shiftKey || + event.altKey + ) { + return; + } const textarea = event.target, - key = event.key.toLowerCase(); + key = event.key.toLowerCase(); for (const id in markdownSyntax) { if (key === markdownSyntax[id].options.shortcutKey) { @@ -242,10 +255,10 @@ function shortcutHandler(event) { } function setupToolbar() { - $$('.communication__toolbar').forEach(toolbar => { + $$('.communication__toolbar').forEach((toolbar) => { toolbar.addEventListener('click', clickHandler); }); - $$('.js-toolbar-input').forEach(textarea => { + $$('.js-toolbar-input').forEach((textarea) => { textarea.addEventListener('keydown', shortcutHandler); }); } diff --git a/assets/js/misc.ts b/assets/js/misc.ts index 25c612120..ef9eb7f98 100644 --- a/assets/js/misc.ts +++ b/assets/js/misc.ts @@ -9,10 +9,10 @@ import '../types/ujs'; let touchMoved = false; -function formResult({target, detail}: FetchcompleteEvent) { +function formResult({ target, detail }: FetchcompleteEvent) { const elements: Record = { '#description-form': '.image-description', - '#uploader-form': '.image-uploader' + '#uploader-form': '.image-uploader', }; function showResult(formEl: HTMLFormElement, resultEl: HTMLElement, response: string) { @@ -20,17 +20,17 @@ function formResult({target, detail}: FetchcompleteEvent) { hideEl(formEl); showEl(resultEl); - $$('input[type="submit"],button', formEl).forEach(button => { + $$('input[type="submit"],button', formEl).forEach((button) => { button.disabled = false; }); } - for (const [ formSelector, resultSelector ] of Object.entries(elements)) { + for (const [formSelector, resultSelector] of Object.entries(elements)) { if (target.matches(formSelector)) { const form = assertType(target, HTMLFormElement); const result = assertNotNull($(resultSelector)); - detail.text().then(text => showResult(form, result, text)); + detail.text().then((text) => showResult(form, result, text)); } } } @@ -85,11 +85,13 @@ export function setupEvents() { } if (store.get('hide_score')) { - $$('.upvotes,.score,.downvotes').forEach(s => hideEl(s)); + $$('.upvotes,.score,.downvotes').forEach((s) => hideEl(s)); } document.addEventListener('fetchcomplete', formResult); document.addEventListener('click', revealSpoiler); document.addEventListener('touchend', revealSpoiler); - document.addEventListener('touchmove', () => touchMoved = true); + document.addEventListener('touchmove', () => { + touchMoved = true; + }); } diff --git a/assets/js/notifications.ts b/assets/js/notifications.ts index d76cf533e..4d4757b9a 100644 --- a/assets/js/notifications.ts +++ b/assets/js/notifications.ts @@ -9,17 +9,17 @@ import { assertNotNull, assertNotUndefined } from './utils/assert'; import store from './utils/store'; const NOTIFICATION_INTERVAL = 600000, - NOTIFICATION_EXPIRES = 300000; + NOTIFICATION_EXPIRES = 300000; function bindSubscriptionLinks() { delegate(document, 'fetchcomplete', { - '.js-subscription-link': event => { + '.js-subscription-link': (event) => { const target = assertNotNull(event.target.closest('.js-subscription-target')); - event.detail.text().then(text => { + event.detail.text().then((text) => { target.outerHTML = text; }); - } + }, }); } @@ -30,7 +30,7 @@ function getNewNotifications() { fetchJson('GET', '/notifications/unread') .then(handleError) - .then(response => response.json()) + .then((response) => response.json()) .then(({ notifications }) => { updateNotificationTicker(notifications); storeNotificationCount(notifications); diff --git a/assets/js/pmwarning.ts b/assets/js/pmwarning.ts index 23772dff1..a8e0bf3fe 100644 --- a/assets/js/pmwarning.ts +++ b/assets/js/pmwarning.ts @@ -18,8 +18,7 @@ export function warnAboutPMs() { if (value.match(imageEmbedRegex)) { showEl(warning); - } - else if (!warning.classList.contains('hidden')) { + } else if (!warning.classList.contains('hidden')) { hideEl(warning); } }); diff --git a/assets/js/preview.js b/assets/js/preview.js index a39687623..6988b3c0a 100644 --- a/assets/js/preview.js +++ b/assets/js/preview.js @@ -47,7 +47,7 @@ function getPreview(body, anonymous, previewLoading, previewIdle, previewContent fetchJson('POST', path, { body, anonymous }) .then(handleError) - .then(data => { + .then((data) => { previewContent.innerHTML = data; filterNode(previewContent); bindImageTarget(previewContent); @@ -110,13 +110,14 @@ function setupPreviews() { // Fire handler for automatic resizing if textarea contains text on page load (e.g. editing) if (textarea.value) textarea.dispatchEvent(new Event('change')); - previewAnon && previewAnon.addEventListener('click', () => { - if (previewContent.classList.contains('hidden')) return; + previewAnon && + previewAnon.addEventListener('click', () => { + if (previewContent.classList.contains('hidden')) return; - updatePreview(); - }); + updatePreview(); + }); - document.addEventListener('click', event => { + document.addEventListener('click', (event) => { if (event.target && event.target.closest('.post-reply')) { const link = event.target.closest('.post-reply'); commentReply(link.dataset.author, link.getAttribute('href'), textarea, link.dataset.post); diff --git a/assets/js/query/__tests__/date.spec.ts b/assets/js/query/__tests__/date.spec.ts index 0c205d4d0..d9f8336d8 100644 --- a/assets/js/query/__tests__/date.spec.ts +++ b/assets/js/query/__tests__/date.spec.ts @@ -97,7 +97,9 @@ describe('Date parsing', () => { }); it('should not match malformed absolute date expressions', () => { - expect(() => makeDateMatcher('2024-06-21T06:21:30+01:3020', 'eq')).toThrow('Cannot parse date string: 2024-06-21T06:21:30+01:3020'); + expect(() => makeDateMatcher('2024-06-21T06:21:30+01:3020', 'eq')).toThrow( + 'Cannot parse date string: 2024-06-21T06:21:30+01:3020', + ); }); it('should not match malformed relative date expressions', () => { diff --git a/assets/js/query/__tests__/user.spec.ts b/assets/js/query/__tests__/user.spec.ts index 52545d0c8..044b13eaa 100644 --- a/assets/js/query/__tests__/user.spec.ts +++ b/assets/js/query/__tests__/user.spec.ts @@ -4,10 +4,10 @@ describe('User field parsing', () => { beforeEach(() => { /* eslint-disable camelcase */ window.booru.interactions = [ - {image_id: 0, user_id: 0, interaction_type: 'faved', value: null}, - {image_id: 0, user_id: 0, interaction_type: 'voted', value: 'up'}, - {image_id: 1, user_id: 0, interaction_type: 'voted', value: 'down'}, - {image_id: 2, user_id: 0, interaction_type: 'hidden', value: null}, + { image_id: 0, user_id: 0, interaction_type: 'faved', value: null }, + { image_id: 0, user_id: 0, interaction_type: 'voted', value: 'up' }, + { image_id: 1, user_id: 0, interaction_type: 'voted', value: 'down' }, + { image_id: 2, user_id: 0, interaction_type: 'hidden', value: null }, ]; /* eslint-enable camelcase */ }); diff --git a/assets/js/query/boolean.ts b/assets/js/query/boolean.ts index 68a0b1073..6f2afe964 100644 --- a/assets/js/query/boolean.ts +++ b/assets/js/query/boolean.ts @@ -1,11 +1,11 @@ import { AstMatcher } from './types'; export function matchAny(...matchers: AstMatcher[]): AstMatcher { - return (e: HTMLElement) => matchers.some(matcher => matcher(e)); + return (e: HTMLElement) => matchers.some((matcher) => matcher(e)); } export function matchAll(...matchers: AstMatcher[]): AstMatcher { - return (e: HTMLElement) => matchers.every(matcher => matcher(e)); + return (e: HTMLElement) => matchers.every((matcher) => matcher(e)); } export function matchNot(matcher: AstMatcher): AstMatcher { diff --git a/assets/js/query/date.ts b/assets/js/query/date.ts index b2dd40339..17b1e5897 100644 --- a/assets/js/query/date.ts +++ b/assets/js/query/date.ts @@ -17,16 +17,16 @@ function makeMatcher(bottomDate: PosixTimeMs, topDate: PosixTimeMs, qual: RangeE // done compared to numeric ranges. switch (qual) { case 'lte': - return v => new Date(v).getTime() < topDate; + return (v) => new Date(v).getTime() < topDate; case 'gte': - return v => new Date(v).getTime() >= bottomDate; + return (v) => new Date(v).getTime() >= bottomDate; case 'lt': - return v => new Date(v).getTime() < bottomDate; + return (v) => new Date(v).getTime() < bottomDate; case 'gt': - return v => new Date(v).getTime() >= topDate; + return (v) => new Date(v).getTime() >= topDate; case 'eq': default: - return v => { + return (v) => { const t = new Date(v).getTime(); return t >= bottomDate && t < topDate; }; @@ -44,7 +44,7 @@ function makeRelativeDateMatcher(dateVal: string, qual: RangeEqualQualifier): Fi day: 86400000, week: 604800000, month: 2592000000, - year: 31536000000 + year: 31536000000, }; const amount = parseInt(match[1], 10); @@ -58,14 +58,7 @@ function makeRelativeDateMatcher(dateVal: string, qual: RangeEqualQualifier): Fi } function makeAbsoluteDateMatcher(dateVal: string, qual: RangeEqualQualifier): FieldMatcher { - const parseRes: RegExp[] = [ - /^(\d{4})/, - /^-(\d{2})/, - /^-(\d{2})/, - /^(?:\s+|T|t)(\d{2})/, - /^:(\d{2})/, - /^:(\d{2})/ - ]; + const parseRes: RegExp[] = [/^(\d{4})/, /^-(\d{2})/, /^-(\d{2})/, /^(?:\s+|T|t)(\d{2})/, /^:(\d{2})/, /^:(\d{2})/]; const timeZoneOffset: TimeZoneOffset = [0, 0]; const timeData: AbsoluteDate = [0, 0, 1, 0, 0, 0]; @@ -81,8 +74,7 @@ function makeAbsoluteDateMatcher(dateVal: string, qual: RangeEqualQualifier): Fi timeZoneOffset[1] *= -1; } localDateVal = localDateVal.substring(0, localDateVal.length - 6); - } - else { + } else { localDateVal = localDateVal.replace(/[Zz]$/, ''); } @@ -97,16 +89,14 @@ function makeAbsoluteDateMatcher(dateVal: string, qual: RangeEqualQualifier): Fi if (matchIndex === 1) { // Months are offset by 1. timeData[matchIndex] = parseInt(componentMatch[1], 10) - 1; - } - else { + } else { // All other components are not offset. timeData[matchIndex] = parseInt(componentMatch[1], 10); } // Truncate string. localDateVal = localDateVal.substring(componentMatch[0].length); - } - else { + } else { throw new ParseError(`Cannot parse date string: ${origDateVal}`); } } diff --git a/assets/js/query/fields.ts b/assets/js/query/fields.ts index 0c1f82e0d..0b666f7c3 100644 --- a/assets/js/query/fields.ts +++ b/assets/js/query/fields.ts @@ -2,16 +2,23 @@ import { FieldName } from './types'; type AttributeName = string; -export const numberFields: FieldName[] = - ['id', 'width', 'height', 'aspect_ratio', - 'comment_count', 'score', 'upvotes', 'downvotes', - 'faves', 'tag_count', 'score']; +export const numberFields: FieldName[] = [ + 'id', + 'width', + 'height', + 'aspect_ratio', + 'comment_count', + 'score', + 'upvotes', + 'downvotes', + 'faves', + 'tag_count', + 'score', +]; export const dateFields: FieldName[] = ['created_at']; -export const literalFields = - ['tags', 'orig_sha512_hash', 'sha512_hash', - 'uploader', 'source_url', 'description']; +export const literalFields = ['tags', 'orig_sha512_hash', 'sha512_hash', 'uploader', 'source_url', 'description']; export const termSpaceToImageField: Record = { tags: 'data-image-tag-aliases', @@ -32,7 +39,7 @@ export const termSpaceToImageField: Record = { faves: 'data-faves', sha512_hash: 'data-sha512', orig_sha512_hash: 'data-orig-sha512', - created_at: 'data-created-at' + created_at: 'data-created-at', /* eslint-enable camelcase */ }; diff --git a/assets/js/query/lex.ts b/assets/js/query/lex.ts index 2c950bd11..80a2ce98b 100644 --- a/assets/js/query/lex.ts +++ b/assets/js/query/lex.ts @@ -17,7 +17,7 @@ const tokenList: Token[] = [ ['not_op', /^\s*[!-]\s*/], ['space', /^\s+/], ['word', /^(?:\\[\s,()^~]|[^\s,()^~])+/], - ['word', /^(?:\\[\s,()]|[^\s,()])+/] + ['word', /^(?:\\[\s,()]|[^\s,()])+/], ]; export type ParseTerm = (term: string, fuzz: number, boost: number) => AstMatcher; @@ -26,14 +26,14 @@ export type Range = [number, number]; export type TermContext = [Range, string]; export interface LexResult { - tokenList: TokenList, - termContexts: TermContext[], - error: ParseError | null + tokenList: TokenList; + termContexts: TermContext[]; + error: ParseError | null; } export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexResult { const opQueue: string[] = [], - groupNegate: boolean[] = []; + groupNegate: boolean[] = []; let searchTerm: string | null = null; let boostFuzzStr = ''; @@ -49,7 +49,7 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR const ret: LexResult = { tokenList: [], termContexts: [], - error: null + error: null, }; const beginTerm = (token: string) => { @@ -86,7 +86,10 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR const token = match[0]; - if (searchTerm !== null && (['and_op', 'or_op'].indexOf(tokenName) !== -1 || tokenName === 'rparen' && lparenCtr === 0)) { + if ( + searchTerm !== null && + (['and_op', 'or_op'].indexOf(tokenName) !== -1 || (tokenName === 'rparen' && lparenCtr === 0)) + ) { endTerm(); } @@ -107,8 +110,7 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR if (searchTerm) { // We're already inside a search term, so it does not apply, obv. searchTerm += token; - } - else { + } else { negate = !negate; } break; @@ -118,8 +120,7 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR // instead, consider it as part of the search term, as a user convenience. searchTerm += token; lparenCtr += 1; - } - else { + } else { opQueue.unshift('lparen'); groupNegate.push(negate); negate = false; @@ -129,8 +130,7 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR if (lparenCtr > 0) { searchTerm = assertNotNull(searchTerm) + token; lparenCtr -= 1; - } - else { + } else { while (opQueue.length > 0) { const op = assertNotUndefined(opQueue.shift()); if (op === 'lparen') { @@ -149,8 +149,7 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR // to a temporary string in case this is actually inside the term. fuzz = parseFloat(token.substring(1)); boostFuzzStr += token; - } - else { + } else { beginTerm(token); } break; @@ -158,16 +157,14 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR if (searchTerm) { boost = parseFloat(token.substring(1)); boostFuzzStr += token; - } - else { + } else { beginTerm(token); } break; case 'quoted_lit': if (searchTerm) { searchTerm += token; - } - else { + } else { beginTerm(token); } break; @@ -180,8 +177,7 @@ export function generateLexResult(searchStr: string, parseTerm: ParseTerm): LexR boostFuzzStr = ''; } searchTerm += token; - } - else { + } else { beginTerm(token); } break; diff --git a/assets/js/query/literal.ts b/assets/js/query/literal.ts index 76bfd54cc..3694a20ff 100644 --- a/assets/js/query/literal.ts +++ b/assets/js/query/literal.ts @@ -23,11 +23,13 @@ function makeWildcardMatcher(term: string): FieldMatcher { // A custom NFA with caching may be more sophisticated but not // likely to be faster. const wildcard = new RegExp( - `^${term.replace(/([.+^$[\]\\(){}|-])/g, '\\$1') + `^${term + .replace(/([.+^$[\]\\(){}|-])/g, '\\$1') .replace(/([^\\]|[^\\](?:\\\\)+)\*/g, '$1.*') .replace(/^(?:\\\\)*\*/g, '.*') .replace(/([^\\]|[^\\](?:\\\\)+)\?/g, '$1.?') - .replace(/^(?:\\\\)*\?/g, '.?')}$`, 'i' + .replace(/^(?:\\\\)*\?/g, '.?')}$`, + 'i', ); return (v, name) => { @@ -69,10 +71,9 @@ function fuzzyMatch(term: string, targetStr: string, fuzz: number): boolean { // Insertion. v2[j] + 1, // Substitution or No Change. - v1[j] + cost + v1[j] + cost, ); - if (i > 1 && j > 1 && term[i] === targetStrLower[j - 1] && - targetStrLower[i - 1] === targetStrLower[j]) { + if (i > 1 && j > 1 && term[i] === targetStrLower[j - 1] && targetStrLower[i - 1] === targetStrLower[j]) { v2[j + 1] = Math.min(v2[j], v0[j - 1] + cost); } } diff --git a/assets/js/query/matcher.ts b/assets/js/query/matcher.ts index d8e67df27..c16b60103 100644 --- a/assets/js/query/matcher.ts +++ b/assets/js/query/matcher.ts @@ -6,10 +6,10 @@ import { makeUserMatcher } from './user'; import { FieldMatcher, RangeEqualQualifier } from './types'; export interface MatcherFactory { - makeDateMatcher: (dateVal: string, qual: RangeEqualQualifier) => FieldMatcher, - makeLiteralMatcher: (term: string, fuzz: number, wildcardable: boolean) => FieldMatcher, - makeNumberMatcher: (term: number, fuzz: number, qual: RangeEqualQualifier) => FieldMatcher, - makeUserMatcher: (term: string) => FieldMatcher + makeDateMatcher: (dateVal: string, qual: RangeEqualQualifier) => FieldMatcher; + makeLiteralMatcher: (term: string, fuzz: number, wildcardable: boolean) => FieldMatcher; + makeNumberMatcher: (term: number, fuzz: number, qual: RangeEqualQualifier) => FieldMatcher; + makeUserMatcher: (term: string) => FieldMatcher; } export const defaultMatcher: MatcherFactory = { diff --git a/assets/js/query/number.ts b/assets/js/query/number.ts index 8c42db300..5fe08e727 100644 --- a/assets/js/query/number.ts +++ b/assets/js/query/number.ts @@ -2,7 +2,7 @@ import { FieldMatcher, RangeEqualQualifier } from './types'; export function makeNumberMatcher(term: number, fuzz: number, qual: RangeEqualQualifier): FieldMatcher { // Range matching. - return v => { + return (v) => { const attrVal = parseFloat(v); if (isNaN(attrVal)) { diff --git a/assets/js/query/parse.ts b/assets/js/query/parse.ts index fea7659b8..7f725c6c1 100644 --- a/assets/js/query/parse.ts +++ b/assets/js/query/parse.ts @@ -23,19 +23,16 @@ export function parseTokens(lexicalArray: TokenList): AstMatcher { if (token === 'and_op') { intermediate = matchAll(op1, op2); - } - else { + } else { intermediate = matchAny(op1, op2); } - } - else { + } else { intermediate = token; } if (lexicalArray[i + 1] === 'not_op') { operandStack.push(matchNot(intermediate)); - } - else { + } else { operandStack.push(intermediate); } } diff --git a/assets/js/query/term.ts b/assets/js/query/term.ts index 22b161eb2..1708b810e 100644 --- a/assets/js/query/term.ts +++ b/assets/js/query/term.ts @@ -67,11 +67,9 @@ function makeTermMatcher(term: string, fuzz: number, factory: MatcherFactory): [ } return [fieldName, factory.makeNumberMatcher(parseFloat(termCandidate), fuzz, rangeType)]; - } - else if (literalFields.indexOf(candidateTermSpace) !== -1) { + } else if (literalFields.indexOf(candidateTermSpace) !== -1) { return [candidateTermSpace, factory.makeLiteralMatcher(termCandidate, fuzz, wildcardable)]; - } - else if (candidateTermSpace === 'my') { + } else if (candidateTermSpace === 'my') { return [candidateTermSpace, factory.makeUserMatcher(termCandidate)]; } } diff --git a/assets/js/query/user.ts b/assets/js/query/user.ts index 3f3834256..2b8dbfe8d 100644 --- a/assets/js/query/user.ts +++ b/assets/js/query/user.ts @@ -1,8 +1,15 @@ import { Interaction, InteractionType, InteractionValue } from '../../types/booru-object'; import { FieldMatcher } from './types'; -function interactionMatch(imageId: number, type: InteractionType, value: InteractionValue, interactions: Interaction[]): boolean { - return interactions.some(v => v.image_id === imageId && v.interaction_type === type && (value === null || v.value === value)); +function interactionMatch( + imageId: number, + type: InteractionType, + value: InteractionValue, + interactions: Interaction[], +): boolean { + return interactions.some( + (v) => v.image_id === imageId && v.interaction_type === type && (value === null || v.value === value), + ); } export function makeUserMatcher(term: string): FieldMatcher { diff --git a/assets/js/quick-tag.js b/assets/js/quick-tag.js index 055c44083..e43b460e7 100644 --- a/assets/js/quick-tag.js +++ b/assets/js/quick-tag.js @@ -9,56 +9,54 @@ import { fetchJson, handleError } from './utils/requests'; const imageQueueStorage = 'quickTagQueue'; const currentTagStorage = 'quickTagName'; -function currentQueue() { return store.get(imageQueueStorage) || []; } +function currentQueue() { + return store.get(imageQueueStorage) || []; +} -function currentTags() { return store.get(currentTagStorage) || ''; } +function currentTags() { + return store.get(currentTagStorage) || ''; +} -function getTagButton() { return $('.js-quick-tag'); } +function getTagButton() { + return $('.js-quick-tag'); +} -function setTagButton(text) { $('.js-quick-tag--submit span').textContent = text; } +function setTagButton(text) { + $('.js-quick-tag--submit span').textContent = text; +} function toggleActiveState() { - - toggleEl($('.js-quick-tag'), - $('.js-quick-tag--abort'), - $('.js-quick-tag--all'), - $('.js-quick-tag--submit')); + toggleEl($('.js-quick-tag'), $('.js-quick-tag--abort'), $('.js-quick-tag--all'), $('.js-quick-tag--submit')); setTagButton(`Submit (${currentTags()})`); - $$('.media-box__header').forEach(el => el.classList.toggle('media-box__header--unselected')); - $$('.media-box__header').forEach(el => el.classList.remove('media-box__header--selected')); - currentQueue().forEach(id => $$(`.media-box__header[data-image-id="${id}"]`).forEach(el => el.classList.add('media-box__header--selected'))); - + $$('.media-box__header').forEach((el) => el.classList.toggle('media-box__header--unselected')); + $$('.media-box__header').forEach((el) => el.classList.remove('media-box__header--selected')); + currentQueue().forEach((id) => + $$(`.media-box__header[data-image-id="${id}"]`).forEach((el) => el.classList.add('media-box__header--selected')), + ); } function activate() { - store.set(currentTagStorage, window.prompt('A comma-delimited list of tags you want to add:')); if (currentTags()) toggleActiveState(); - } function reset() { - store.remove(currentTagStorage); store.remove(imageQueueStorage); toggleActiveState(); - } function promptReset() { - if (window.confirm('Are you sure you want to abort batch tagging?')) { reset(); } - } function submit() { - setTagButton(`Wait... (${currentTags()})`); fetchJson('PUT', '/admin/batch/tags', { @@ -66,32 +64,28 @@ function submit() { image_ids: currentQueue(), }) .then(handleError) - .then(r => r.json()) - .then(data => { - + .then((r) => r.json()) + .then((data) => { if (data.failed.length) window.alert(`Failed to add tags to the images with these IDs: ${data.failed}`); reset(); - }); - } function modifyImageQueue(mediaBox) { - if (currentTags()) { - const imageId = mediaBox.dataset.imageId, - queue = currentQueue(), - isSelected = queue.includes(imageId); + const imageId = mediaBox.dataset.imageId, + queue = currentQueue(), + isSelected = queue.includes(imageId); - isSelected ? queue.splice(queue.indexOf(imageId), 1) - : queue.push(imageId); + isSelected ? queue.splice(queue.indexOf(imageId), 1) : queue.push(imageId); - $$(`.media-box__header[data-image-id="${imageId}"]`).forEach(el => el.classList.toggle('media-box__header--selected')); + $$(`.media-box__header[data-image-id="${imageId}"]`).forEach((el) => + el.classList.toggle('media-box__header--selected'), + ); store.set(imageQueueStorage, queue); } - } function toggleAllImages() { @@ -99,7 +93,6 @@ function toggleAllImages() { } function clickHandler(event) { - const targets = { '.js-quick-tag': activate, '.js-quick-tag--abort': promptReset, @@ -114,14 +107,11 @@ function clickHandler(event) { currentTags() && event.preventDefault(); } } - } function setupQuickTag() { - if (getTagButton() && currentTags()) toggleActiveState(); if (getTagButton()) onLeftClick(clickHandler); - } export { setupQuickTag }; diff --git a/assets/js/search.ts b/assets/js/search.ts index 7de65e5aa..351105ea8 100644 --- a/assets/js/search.ts +++ b/assets/js/search.ts @@ -2,7 +2,7 @@ import { $, $$ } from './utils/dom'; import { addTag } from './tagsinput'; function showHelp(subject: string, type: string | null) { - $$('[data-search-help]').forEach(helpBox => { + $$('[data-search-help]').forEach((helpBox) => { if (helpBox.getAttribute('data-search-help') === type) { const searchSubject = $('.js-search-help-subject', helpBox); @@ -11,8 +11,7 @@ function showHelp(subject: string, type: string | null) { } helpBox.classList.remove('hidden'); - } - else { + } else { helpBox.classList.add('hidden'); } }); @@ -21,7 +20,8 @@ function showHelp(subject: string, type: string | null) { function prependToLast(field: HTMLInputElement, value: string) { const separatorIndex = field.value.lastIndexOf(','); const advanceBy = field.value[separatorIndex + 1] === ' ' ? 2 : 1; - field.value = field.value.slice(0, separatorIndex + advanceBy) + value + field.value.slice(separatorIndex + advanceBy); + field.value = + field.value.slice(0, separatorIndex + advanceBy) + value + field.value.slice(separatorIndex + advanceBy); } function selectLast(field: HTMLInputElement, characterCount: number) { @@ -32,14 +32,18 @@ function selectLast(field: HTMLInputElement, characterCount: number) { } function executeFormHelper(e: PointerEvent) { - if (!e.target) { return; } + if (!e.target) { + return; + } const searchField = $('.js-search-field'); const attr = (name: string) => e.target && (e.target as HTMLElement).getAttribute(name); if (attr('data-search-add')) addTag(searchField, attr('data-search-add')); if (attr('data-search-show-help')) showHelp((e.target as Node).textContent || '', attr('data-search-show-help')); - if (attr('data-search-select-last') && searchField) selectLast(searchField, parseInt(attr('data-search-select-last') || '', 10)); + if (attr('data-search-select-last') && searchField) { + selectLast(searchField, parseInt(attr('data-search-select-last') || '', 10)); + } if (attr('data-search-prepend') && searchField) prependToLast(searchField, attr('data-search-prepend') || ''); } diff --git a/assets/js/settings.ts b/assets/js/settings.ts index 0bc8a9d7f..ed4256b4d 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -7,21 +7,18 @@ import { $, $$ } from './utils/dom'; import store from './utils/store'; export function setupSettings() { - if (!$('#js-setting-table')) return; const localCheckboxes = $$('[data-tab="local"] input[type="checkbox"]'); const themeSelect = assertNotNull($('#user_theme_name')); const themeColorSelect = assertNotNull($('#user_theme_color')); const themePaths: Record = JSON.parse( - assertNotUndefined( - assertNotNull($('#js-theme-paths')).dataset.themePaths - ) + assertNotUndefined(assertNotNull($('#js-theme-paths')).dataset.themePaths), ); const styleSheet = assertNotNull($('#js-theme-stylesheet')); // Local settings - localCheckboxes.forEach(checkbox => { + localCheckboxes.forEach((checkbox) => { checkbox.addEventListener('change', () => { store.set(checkbox.id.replace('user_', ''), checkbox.checked); }); diff --git a/assets/js/shortcuts.ts b/assets/js/shortcuts.ts index 48551a3b6..dc7d920fe 100644 --- a/assets/js/shortcuts.ts +++ b/assets/js/shortcuts.ts @@ -37,28 +37,48 @@ function click(selector: string) { } function isOK(event: KeyboardEvent): boolean { - return !event.altKey && !event.ctrlKey && !event.metaKey && - document.activeElement !== null && - document.activeElement.tagName !== 'INPUT' && - document.activeElement.tagName !== 'TEXTAREA'; + return ( + !event.altKey && + !event.ctrlKey && + !event.metaKey && + document.activeElement !== null && + document.activeElement.tagName !== 'INPUT' && + document.activeElement.tagName !== 'TEXTAREA' + ); } const keyCodes: ShortcutKeyMap = { - 'j'() { click('.js-prev'); }, // J - go to previous image - 'i'() { click('.js-up'); }, // I - go to index page - 'k'() { click('.js-next'); }, // K - go to next image - 'r'() { click('.js-rand'); }, // R - go to random image - 's'() { click('.js-source-link'); }, // S - go to image source - 'l'() { click('.js-tag-sauce-toggle'); }, // L - edit tags - 'o'() { openFullView(); }, // O - open original - 'v'() { openFullViewNewTab(); }, // V - open original in a new tab - 'f'() { // F - favourite image - click(getHover() ? `a.interaction--fave[data-image-id="${getHover()}"]` - : '.block__header a.interaction--fave'); + j() { + click('.js-prev'); + }, // J - go to previous image + i() { + click('.js-up'); + }, // I - go to index page + k() { + click('.js-next'); + }, // K - go to next image + r() { + click('.js-rand'); + }, // R - go to random image + s() { + click('.js-source-link'); + }, // S - go to image source + l() { + click('.js-tag-sauce-toggle'); + }, // L - edit tags + o() { + openFullView(); + }, // O - open original + v() { + openFullViewNewTab(); + }, // V - open original in a new tab + f() { + // F - favourite image + click(getHover() ? `a.interaction--fave[data-image-id="${getHover()}"]` : '.block__header a.interaction--fave'); }, - 'u'() { // U - upvote image - click(getHover() ? `a.interaction--upvote[data-image-id="${getHover()}"]` - : '.block__header a.interaction--upvote'); + u() { + // U - upvote image + click(getHover() ? `a.interaction--upvote[data-image-id="${getHover()}"]` : '.block__header a.interaction--upvote'); }, }; diff --git a/assets/js/slider.ts b/assets/js/slider.ts index 923044884..95ca83fc6 100644 --- a/assets/js/slider.ts +++ b/assets/js/slider.ts @@ -34,8 +34,7 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: function clampValue(value: number): number { if (cachedValue >= cachedLimit && value < cachedLimit) { return cachedLimit; - } - else if (cachedValue < cachedLimit && value >= cachedLimit) { + } else if (cachedValue < cachedLimit && value >= cachedLimit) { return cachedLimit - 1; // Offset by 1 to ensure stored value is less than limit. } @@ -55,7 +54,9 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: // Initializes cached variables. Should be used // when the pointer event begins. function initVars() { - if (!parent) { return; } + if (!parent) { + return; + } const rect = parent.getBoundingClientRect(); @@ -70,7 +71,9 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: // Called during pointer movement. function dragMove(e: PointerEvent) { - if (!dragging) { return; } + if (!dragging) { + return; + } e.preventDefault(); @@ -79,13 +82,7 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: // `lerp` cleverly clamps the value between min and max, // so no need for any explicit checks for that here, only // the crossover check is required. - curValue = clampValue( - lerp( - (desiredPos - minPos) / (maxPos - minPos), - cachedMin, - cachedMax - ) - ); + curValue = clampValue(lerp((desiredPos - minPos) / (maxPos - minPos), cachedMin, cachedMax)); // Same here, lerp clamps the value so it doesn't get out // of the slider boundary. @@ -99,7 +96,9 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: // Called when the pointer is let go of. function dragEnd(e: PointerEvent) { - if (!dragging) { return; } + if (!dragging) { + return; + } e.preventDefault(); @@ -111,7 +110,9 @@ function setupDrag(el: HTMLDivElement, dataEl: HTMLInputElement, valueProperty: // Called when the slider head is clicked or tapped. function dragBegin(e: PointerEvent) { - if (!parent) { return; } + if (!parent) { + return; + } e.preventDefault(); initVars(); @@ -168,7 +169,7 @@ function setupSlider(el: HTMLInputElement) { // Sets up all sliders currently on the page. function setupSliders() { - $$('input[type="dualrange"]').forEach(el => { + $$('input[type="dualrange"]').forEach((el) => { setupSlider(el); }); } diff --git a/assets/js/sources.ts b/assets/js/sources.ts index b4bc51ecf..0a0aab18c 100644 --- a/assets/js/sources.ts +++ b/assets/js/sources.ts @@ -19,7 +19,7 @@ export function imageSourcesCreator() { if (target.matches('#source-form')) { const sourceSauce = assertNotNull($('.js-sourcesauce')); - detail.text().then(text => { + detail.text().then((text) => { sourceSauce.outerHTML = text; setupInputs(); }); diff --git a/assets/js/staffhider.ts b/assets/js/staffhider.ts index 86741d785..23aa38990 100644 --- a/assets/js/staffhider.ts +++ b/assets/js/staffhider.ts @@ -8,6 +8,6 @@ import { $$, hideEl } from './utils/dom'; export function hideStaffTools() { if (window.booru.hideStaffTools === 'true') { - $$('.js-staff-action').forEach(el => hideEl(el)); + $$('.js-staff-action').forEach((el) => hideEl(el)); } } diff --git a/assets/js/tags.ts b/assets/js/tags.ts index c3547f27b..4e7f9191d 100644 --- a/assets/js/tags.ts +++ b/assets/js/tags.ts @@ -19,44 +19,70 @@ function removeTag(tagId: number, list: number[]) { function createTagDropdown(tag: HTMLSpanElement) { const { userIsSignedIn, userCanEditFilter, watchedTagList, spoileredTagList, hiddenTagList } = window.booru; - const [ unwatch, watch, unspoiler, spoiler, unhide, hide, signIn, filter ] = $$('.tag__dropdown__link', tag); - const [ unwatched, watched, spoilered, hidden ] = $$('.tag__state', tag); + const [unwatch, watch, unspoiler, spoiler, unhide, hide, signIn, filter] = $$( + '.tag__dropdown__link', + tag, + ); + const [unwatched, watched, spoilered, hidden] = $$('.tag__state', tag); const tagId = parseInt(assertNotUndefined(tag.dataset.tagId), 10); const actions: TagDropdownActionList = { - unwatch() { hideEl(unwatch, watched); showEl(watch, unwatched); removeTag(tagId, watchedTagList); }, - watch() { hideEl(watch, unwatched); showEl(unwatch, watched); addTag(tagId, watchedTagList); }, + unwatch() { + hideEl(unwatch, watched); + showEl(watch, unwatched); + removeTag(tagId, watchedTagList); + }, + watch() { + hideEl(watch, unwatched); + showEl(unwatch, watched); + addTag(tagId, watchedTagList); + }, - unspoiler() { hideEl(unspoiler, spoilered); showEl(spoiler); removeTag(tagId, spoileredTagList); }, - spoiler() { hideEl(spoiler); showEl(unspoiler, spoilered); addTag(tagId, spoileredTagList); }, + unspoiler() { + hideEl(unspoiler, spoilered); + showEl(spoiler); + removeTag(tagId, spoileredTagList); + }, + spoiler() { + hideEl(spoiler); + showEl(unspoiler, spoilered); + addTag(tagId, spoileredTagList); + }, - unhide() { hideEl(unhide, hidden); showEl(hide); removeTag(tagId, hiddenTagList); }, - hide() { hideEl(hide); showEl(unhide, hidden); addTag(tagId, hiddenTagList); }, + unhide() { + hideEl(unhide, hidden); + showEl(hide); + removeTag(tagId, hiddenTagList); + }, + hide() { + hideEl(hide); + showEl(unhide, hidden); + addTag(tagId, hiddenTagList); + }, }; - const tagIsWatched = watchedTagList.includes(tagId); + const tagIsWatched = watchedTagList.includes(tagId); const tagIsSpoilered = spoileredTagList.includes(tagId); - const tagIsHidden = hiddenTagList.includes(tagId); + const tagIsHidden = hiddenTagList.includes(tagId); - const watchedLink = tagIsWatched ? unwatch : watch; - const spoilerLink = tagIsSpoilered ? unspoiler : spoiler; - const hiddenLink = tagIsHidden ? unhide : hide; + const watchedLink = tagIsWatched ? unwatch : watch; + const spoilerLink = tagIsSpoilered ? unspoiler : spoiler; + const hiddenLink = tagIsHidden ? unhide : hide; // State symbols (-, S, H, +) - if (tagIsWatched) showEl(watched); - if (tagIsSpoilered) showEl(spoilered); - if (tagIsHidden) showEl(hidden); - if (!tagIsWatched) showEl(unwatched); + if (tagIsWatched) showEl(watched); + if (tagIsSpoilered) showEl(spoilered); + if (tagIsHidden) showEl(hidden); + if (!tagIsWatched) showEl(unwatched); // Dropdown links - if (userIsSignedIn) showEl(watchedLink); + if (userIsSignedIn) showEl(watchedLink); if (userCanEditFilter) showEl(spoilerLink); if (userCanEditFilter) showEl(hiddenLink); - if (!userIsSignedIn) showEl(signIn); - if (userIsSignedIn && - !userCanEditFilter) showEl(filter); + if (!userIsSignedIn) showEl(signIn); + if (userIsSignedIn && !userCanEditFilter) showEl(filter); - tag.addEventListener('fetchcomplete', event => { + tag.addEventListener('fetchcomplete', (event) => { const act = assertNotUndefined(event.target.dataset.tagAction); actions[act](); }); diff --git a/assets/js/tagsinput.js b/assets/js/tagsinput.js index 1f6963ac3..10e9fd61d 100644 --- a/assets/js/tagsinput.js +++ b/assets/js/tagsinput.js @@ -5,7 +5,7 @@ import { $, $$, clearEl, removeEl, showEl, hideEl, escapeCss, escapeHtml } from './utils/dom'; function setupTagsInput(tagBlock) { - const [ textarea, container ] = $$('.js-taginput', tagBlock); + const [textarea, container] = $$('.js-taginput', tagBlock); const setup = $('.js-tag-block ~ button', tagBlock.parentNode); const inputField = $('input', container); @@ -42,7 +42,6 @@ function setupTagsInput(tagBlock) { importTags(); } - function handleAutocomplete(event) { insertTag(event.detail.value); inputField.focus(); @@ -82,10 +81,9 @@ function setupTagsInput(tagBlock) { // enter or comma if (keyCode === 13 || (keyCode === 188 && !shiftKey)) { event.preventDefault(); - inputField.value.split(',').forEach(t => insertTag(t)); + inputField.value.split(',').forEach((t) => insertTag(t)); inputField.value = ''; } - } function handleCtrlEnter(event) { @@ -131,19 +129,21 @@ function setupTagsInput(tagBlock) { container.appendChild(inputField); tags = []; - textarea.value.split(',').forEach(t => insertTag(t)); + textarea.value.split(',').forEach((t) => insertTag(t)); textarea.value = tags.join(', '); } } function fancyEditorRequested(tagBlock) { // Check whether the user made the fancy editor the default for each type of tag block. - return window.booru.fancyTagUpload && tagBlock.classList.contains('fancy-tag-upload') || - window.booru.fancyTagEdit && tagBlock.classList.contains('fancy-tag-edit'); + return ( + (window.booru.fancyTagUpload && tagBlock.classList.contains('fancy-tag-upload')) || + (window.booru.fancyTagEdit && tagBlock.classList.contains('fancy-tag-edit')) + ); } function setupTagListener() { - document.addEventListener('addtag', event => { + document.addEventListener('addtag', (event) => { if (event.target.value) event.target.value += ', '; event.target.value += event.detail.name; }); diff --git a/assets/js/tagsmisc.ts b/assets/js/tagsmisc.ts index 093663868..acf657029 100644 --- a/assets/js/tagsmisc.ts +++ b/assets/js/tagsmisc.ts @@ -30,7 +30,7 @@ function tagInputButtons(event: MouseEvent) { }, }; - for (const [ name, action ] of Object.entries(actions)) { + for (const [name, action] of Object.entries(actions)) { if (target && target.matches(`#tagsinput-${name}`)) { action(assertNotNull($('#image_tag_input'))); } @@ -38,7 +38,7 @@ function tagInputButtons(event: MouseEvent) { } function setupTags() { - $$('.js-tag-block').forEach(el => { + $$('.js-tag-block').forEach((el) => { setupTagsInput(el); el.classList.remove('js-tag-block'); }); @@ -48,7 +48,7 @@ function updateTagSauce({ target, detail }: FetchcompleteEvent) { if (target.matches('#tags-form')) { const tagSauce = assertNotNull($('.js-tagsauce')); - detail.text().then(text => { + detail.text().then((text) => { tagSauce.outerHTML = text; setupTags(); initTagDropdown(); diff --git a/assets/js/timeago.ts b/assets/js/timeago.ts index 12f39bd9f..3eb8ec6a0 100644 --- a/assets/js/timeago.ts +++ b/assets/js/timeago.ts @@ -36,24 +36,24 @@ function setTimeAgo(el: HTMLTimeElement) { const distMillis = distance(date); const seconds = Math.abs(distMillis) / 1000, - minutes = seconds / 60, - hours = minutes / 60, - days = hours / 24, - months = days / 30, - years = days / 365; + minutes = seconds / 60, + hours = minutes / 60, + days = hours / 24, + months = days / 30, + years = days / 365; const words = - seconds < 45 && substitute('seconds', seconds) || - seconds < 90 && substitute('minute', 1) || - minutes < 45 && substitute('minutes', minutes) || - minutes < 90 && substitute('hour', 1) || - hours < 24 && substitute('hours', hours) || - hours < 42 && substitute('day', 1) || - days < 30 && substitute('days', days) || - days < 45 && substitute('month', 1) || - days < 365 && substitute('months', months) || - years < 1.5 && substitute('year', 1) || - substitute('years', years); + (seconds < 45 && substitute('seconds', seconds)) || + (seconds < 90 && substitute('minute', 1)) || + (minutes < 45 && substitute('minutes', minutes)) || + (minutes < 90 && substitute('hour', 1)) || + (hours < 24 && substitute('hours', hours)) || + (hours < 42 && substitute('day', 1)) || + (days < 30 && substitute('days', days)) || + (days < 45 && substitute('month', 1)) || + (days < 365 && substitute('months', months)) || + (years < 1.5 && substitute('year', 1)) || + substitute('years', years); if (!el.getAttribute('title')) { el.setAttribute('title', assertNotNull(el.textContent)); diff --git a/assets/js/ujs.ts b/assets/js/ujs.ts index 413bc6cb5..626f8c991 100644 --- a/assets/js/ujs.ts +++ b/assets/js/ujs.ts @@ -4,7 +4,7 @@ import { fire, delegate, leftClick } from './utils/events'; const headers = () => ({ 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'XMLHttpRequest' + 'x-requested-with': 'XMLHttpRequest', }); function confirm(event: Event, target: HTMLElement) { @@ -25,8 +25,7 @@ function disable(event: Event, target: HTMLAnchorElement | HTMLButtonElement | H if (label) { target.dataset.enableWith = assertNotNull(label.nodeValue); label.nodeValue = ` ${target.dataset.disableWith}`; - } - else { + } else { target.dataset.enableWith = target.innerHTML; target.innerHTML = assertNotUndefined(target.dataset.disableWith); } @@ -39,8 +38,8 @@ function disable(event: Event, target: HTMLAnchorElement | HTMLButtonElement | H function linkMethod(event: Event, target: HTMLAnchorElement) { event.preventDefault(); - const form = makeEl('form', { action: target.href, method: 'POST' }); - const csrf = makeEl('input', { type: 'hidden', name: '_csrf_token', value: window.booru.csrfToken }); + const form = makeEl('form', { action: target.href, method: 'POST' }); + const csrf = makeEl('input', { type: 'hidden', name: '_csrf_token', value: window.booru.csrfToken }); const method = makeEl('input', { type: 'hidden', name: '_method', value: target.dataset.method }); document.body.appendChild(form); @@ -57,8 +56,8 @@ function formRemote(event: Event, target: HTMLFormElement) { credentials: 'same-origin', method: (target.dataset.method || target.method).toUpperCase(), headers: headers(), - body: new FormData(target) - }).then(response => { + body: new FormData(target), + }).then((response) => { fire(target, 'fetchcomplete', response); if (response && response.status === 300) { window.location.reload(); @@ -67,12 +66,11 @@ function formRemote(event: Event, target: HTMLFormElement) { } function formReset(_event: Event | null, target: HTMLElement) { - $$('[disabled][data-disable-with][data-enable-with]', target).forEach(input => { + $$('[disabled][data-disable-with][data-enable-with]', target).forEach((input) => { const label = findFirstTextNode(input); if (label) { label.nodeValue = ` ${input.dataset.enableWith}`; - } - else { + } else { input.innerHTML = assertNotUndefined(input.dataset.enableWith); } delete input.dataset.enableWith; @@ -86,10 +84,8 @@ function linkRemote(event: Event, target: HTMLAnchorElement) { fetch(target.href, { credentials: 'same-origin', method: (target.dataset.method || 'get').toUpperCase(), - headers: headers() - }).then(response => - fire(target, 'fetchcomplete', response) - ); + headers: headers(), + }).then((response) => fire(target, 'fetchcomplete', response)); } delegate(document, 'click', { @@ -100,11 +96,11 @@ delegate(document, 'click', { }); delegate(document, 'submit', { - 'form[data-remote]': formRemote + 'form[data-remote]': formRemote, }); delegate(document, 'reset', { - form: formReset + form: formReset, }); window.addEventListener('pageshow', () => { diff --git a/assets/js/upload.js b/assets/js/upload.js index 62f749fbd..6d0e52b53 100644 --- a/assets/js/upload.js +++ b/assets/js/upload.js @@ -11,7 +11,7 @@ const MATROSKA_MAGIC = 0x1a45dfa3; function scrapeUrl(url) { return fetchJson('POST', '/images/scrape', { url }) .then(handleError) - .then(response => response.json()); + .then((response) => response.json()); } function elementForEmbeddedImage({ camo_url, type }) { @@ -34,7 +34,7 @@ function setupImageUpload() { const [fileField, remoteUrl, scraperError] = $$('.js-scraper', form); const descrEl = $('.js-image-descr-input', form); const tagsEl = $('.js-image-tags-input', form); - const sourceEl = $$('.js-source-url', form).find(input => input.value === ''); + const sourceEl = $$('.js-source-url', form).find((input) => input.value === ''); const fetchButton = $('#js-scraper-preview'); if (!fetchButton) return; @@ -68,17 +68,25 @@ function setupImageUpload() { showEl(scraperError); enableFetch(); } - function hideError() { hideEl(scraperError); } - function disableFetch() { fetchButton.setAttribute('disabled', ''); } - function enableFetch() { fetchButton.removeAttribute('disabled'); } + function hideError() { + hideEl(scraperError); + } + function disableFetch() { + fetchButton.setAttribute('disabled', ''); + } + function enableFetch() { + fetchButton.removeAttribute('disabled'); + } const reader = new FileReader(); - reader.addEventListener('load', event => { - showImages([{ - camo_url: event.target.result, - type: fileField.files[0].type - }]); + reader.addEventListener('load', (event) => { + showImages([ + { + camo_url: event.target.result, + type: fileField.files[0].type, + }, + ]); // Clear any currently cached data, because the file field // has higher priority than the scraper: @@ -88,7 +96,9 @@ function setupImageUpload() { }); // Watch for files added to the form - fileField.addEventListener('change', () => { fileField.files.length && reader.readAsArrayBuffer(fileField.files[0]); }); + fileField.addEventListener('change', () => { + fileField.files.length && reader.readAsArrayBuffer(fileField.files[0]); + }); // Watch for [Fetch] clicks fetchButton.addEventListener('click', () => { @@ -96,37 +106,39 @@ function setupImageUpload() { disableFetch(); - scrapeUrl(remoteUrl.value).then(data => { - if (data === null) { - scraperError.innerText = 'No image found at that address.'; - showError(); - return; - } - else if (data.errors && data.errors.length > 0) { - scraperError.innerText = data.errors.join(' '); - showError(); - return; - } - - hideError(); - - // Set source - if (sourceEl) sourceEl.value = sourceEl.value || data.source_url || ''; - // Set description - if (descrEl) descrEl.value = descrEl.value || data.description || ''; - // Add author - if (tagsEl && data.author_name) addTag(tagsEl, `artist:${data.author_name.toLowerCase()}`); - // Clear selected file, if any - fileField.value = ''; - showImages(data.images); - - enableFetch(); - }).catch(showError); + scrapeUrl(remoteUrl.value) + .then((data) => { + if (data === null) { + scraperError.innerText = 'No image found at that address.'; + showError(); + return; + } else if (data.errors && data.errors.length > 0) { + scraperError.innerText = data.errors.join(' '); + showError(); + return; + } + + hideError(); + + // Set source + if (sourceEl) sourceEl.value = sourceEl.value || data.source_url || ''; + // Set description + if (descrEl) descrEl.value = descrEl.value || data.description || ''; + // Add author + if (tagsEl && data.author_name) addTag(tagsEl, `artist:${data.author_name.toLowerCase()}`); + // Clear selected file, if any + fileField.value = ''; + showImages(data.images); + + enableFetch(); + }) + .catch(showError); }); // Fetch on "enter" in url field - remoteUrl.addEventListener('keydown', event => { - if (event.keyCode === 13) { // Hit enter + remoteUrl.addEventListener('keydown', (event) => { + if (event.keyCode === 13) { + // Hit enter fetchButton.click(); } }); @@ -135,8 +147,7 @@ function setupImageUpload() { function setFetchEnabled() { if (remoteUrl.value.length > 0) { enableFetch(); - } - else { + } else { disableFetch(); } } diff --git a/assets/js/utils/__tests__/array.spec.ts b/assets/js/utils/__tests__/array.spec.ts index 4b1a9501c..89e8f76d2 100644 --- a/assets/js/utils/__tests__/array.spec.ts +++ b/assets/js/utils/__tests__/array.spec.ts @@ -84,15 +84,17 @@ describe('Array Utilities', () => { // Mixed parameters const mockObject = { value: Math.random() }; - expect(arraysEqual( - ['', null, false, uniqueValue, mockObject, Infinity, undefined], - ['', null, false, uniqueValue, mockObject, Infinity, undefined] - )).toBe(true); + expect( + arraysEqual( + ['', null, false, uniqueValue, mockObject, Infinity, undefined], + ['', null, false, uniqueValue, mockObject, Infinity, undefined], + ), + ).toBe(true); }); }); describe('negative cases', () => { - it('should NOT return true for matching only up to the first array\'s length', () => { + it("should NOT return true for matching only up to the first array's length", () => { // Numbers expect(arraysEqual([0], [0, 1])).toBe(false); expect(arraysEqual([0, 1], [0, 1, 2])).toBe(false); @@ -108,26 +110,15 @@ describe('Array Utilities', () => { // Mixed parameters const mockObject = { value: Math.random() }; - expect(arraysEqual( - [''], - ['', null, false, mockObject, Infinity, undefined] - )).toBe(false); - expect(arraysEqual( - ['', null], - ['', null, false, mockObject, Infinity, undefined] - )).toBe(false); - expect(arraysEqual( - ['', null, false], - ['', null, false, mockObject, Infinity, undefined] - )).toBe(false); - expect(arraysEqual( - ['', null, false, mockObject], - ['', null, false, mockObject, Infinity, undefined] - )).toBe(false); - expect(arraysEqual( - ['', null, false, mockObject, Infinity], - ['', null, false, mockObject, Infinity, undefined] - )).toBe(false); + expect(arraysEqual([''], ['', null, false, mockObject, Infinity, undefined])).toBe(false); + expect(arraysEqual(['', null], ['', null, false, mockObject, Infinity, undefined])).toBe(false); + expect(arraysEqual(['', null, false], ['', null, false, mockObject, Infinity, undefined])).toBe(false); + expect(arraysEqual(['', null, false, mockObject], ['', null, false, mockObject, Infinity, undefined])).toBe( + false, + ); + expect( + arraysEqual(['', null, false, mockObject, Infinity], ['', null, false, mockObject, Infinity, undefined]), + ).toBe(false); }); it('should return false for arrays of different length', () => { @@ -151,7 +142,7 @@ describe('Array Utilities', () => { expect(arraysEqual([mockObject], [mockObject, mockObject])).toBe(false); }); - it('should return false if items up to the first array\'s length differ', () => { + it("should return false if items up to the first array's length differ", () => { // Numbers expect(arraysEqual([0], [1])).toBe(false); expect(arraysEqual([0, 1], [1, 2])).toBe(false); @@ -168,22 +159,12 @@ describe('Array Utilities', () => { expect(arraysEqual([mockObject1], [mockObject2])).toBe(false); // Mixed parameters - expect(arraysEqual( - ['a'], - ['b', null, false, mockObject2, Infinity] - )).toBe(false); - expect(arraysEqual( - ['a', null, true], - ['b', null, false, mockObject2, Infinity] - )).toBe(false); - expect(arraysEqual( - ['a', null, true, mockObject1], - ['b', null, false, mockObject2, Infinity] - )).toBe(false); - expect(arraysEqual( - ['a', null, true, mockObject1, -Infinity], - ['b', null, false, mockObject2, Infinity] - )).toBe(false); + expect(arraysEqual(['a'], ['b', null, false, mockObject2, Infinity])).toBe(false); + expect(arraysEqual(['a', null, true], ['b', null, false, mockObject2, Infinity])).toBe(false); + expect(arraysEqual(['a', null, true, mockObject1], ['b', null, false, mockObject2, Infinity])).toBe(false); + expect(arraysEqual(['a', null, true, mockObject1, -Infinity], ['b', null, false, mockObject2, Infinity])).toBe( + false, + ); }); }); }); diff --git a/assets/js/utils/__tests__/dom.spec.ts b/assets/js/utils/__tests__/dom.spec.ts index a1bb03eb3..dfd0faa21 100644 --- a/assets/js/utils/__tests__/dom.spec.ts +++ b/assets/js/utils/__tests__/dom.spec.ts @@ -87,11 +87,7 @@ describe('DOM Utilities', () => { }); it(`should remove the ${hiddenClass} class from all provided elements`, () => { - const mockElements = [ - createHiddenElement('div'), - createHiddenElement('a'), - createHiddenElement('strong'), - ]; + const mockElements = [createHiddenElement('div'), createHiddenElement('a'), createHiddenElement('strong')]; showEl(mockElements); expect(mockElements[0]).not.toHaveClass(hiddenClass); expect(mockElements[1]).not.toHaveClass(hiddenClass); @@ -99,14 +95,8 @@ describe('DOM Utilities', () => { }); it(`should remove the ${hiddenClass} class from elements provided in multiple arrays`, () => { - const mockElements1 = [ - createHiddenElement('div'), - createHiddenElement('a'), - ]; - const mockElements2 = [ - createHiddenElement('strong'), - createHiddenElement('em'), - ]; + const mockElements1 = [createHiddenElement('div'), createHiddenElement('a')]; + const mockElements2 = [createHiddenElement('strong'), createHiddenElement('em')]; showEl(mockElements1, mockElements2); expect(mockElements1[0]).not.toHaveClass(hiddenClass); expect(mockElements1[1]).not.toHaveClass(hiddenClass); @@ -135,14 +125,8 @@ describe('DOM Utilities', () => { }); it(`should add the ${hiddenClass} class to elements provided in multiple arrays`, () => { - const mockElements1 = [ - document.createElement('div'), - document.createElement('a'), - ]; - const mockElements2 = [ - document.createElement('strong'), - document.createElement('em'), - ]; + const mockElements1 = [document.createElement('div'), document.createElement('a')]; + const mockElements2 = [document.createElement('strong'), document.createElement('em')]; hideEl(mockElements1, mockElements2); expect(mockElements1[0]).toHaveClass(hiddenClass); expect(mockElements1[1]).toHaveClass(hiddenClass); @@ -159,24 +143,15 @@ describe('DOM Utilities', () => { }); it('should set the disabled attribute to true on all provided elements', () => { - const mockElements = [ - document.createElement('input'), - document.createElement('button'), - ]; + const mockElements = [document.createElement('input'), document.createElement('button')]; disableEl(mockElements); expect(mockElements[0]).toBeDisabled(); expect(mockElements[1]).toBeDisabled(); }); it('should set the disabled attribute to true on elements provided in multiple arrays', () => { - const mockElements1 = [ - document.createElement('input'), - document.createElement('button'), - ]; - const mockElements2 = [ - document.createElement('textarea'), - document.createElement('button'), - ]; + const mockElements1 = [document.createElement('input'), document.createElement('button')]; + const mockElements2 = [document.createElement('textarea'), document.createElement('button')]; disableEl(mockElements1, mockElements2); expect(mockElements1[0]).toBeDisabled(); expect(mockElements1[1]).toBeDisabled(); @@ -193,24 +168,15 @@ describe('DOM Utilities', () => { }); it('should set the disabled attribute to false on all provided elements', () => { - const mockElements = [ - document.createElement('input'), - document.createElement('button'), - ]; + const mockElements = [document.createElement('input'), document.createElement('button')]; enableEl(mockElements); expect(mockElements[0]).toBeEnabled(); expect(mockElements[1]).toBeEnabled(); }); it('should set the disabled attribute to false on elements provided in multiple arrays', () => { - const mockElements1 = [ - document.createElement('input'), - document.createElement('button'), - ]; - const mockElements2 = [ - document.createElement('textarea'), - document.createElement('button'), - ]; + const mockElements1 = [document.createElement('input'), document.createElement('button')]; + const mockElements2 = [document.createElement('textarea'), document.createElement('button')]; enableEl(mockElements1, mockElements2); expect(mockElements1[0]).toBeEnabled(); expect(mockElements1[1]).toBeEnabled(); @@ -245,14 +211,8 @@ describe('DOM Utilities', () => { }); it(`should toggle the ${hiddenClass} class on elements provided in multiple arrays`, () => { - const mockElements1 = [ - createHiddenElement('div'), - document.createElement('a'), - ]; - const mockElements2 = [ - createHiddenElement('strong'), - document.createElement('em'), - ]; + const mockElements1 = [createHiddenElement('div'), document.createElement('a')]; + const mockElements2 = [createHiddenElement('strong'), document.createElement('em')]; toggleEl(mockElements1, mockElements2); expect(mockElements1[0]).not.toHaveClass(hiddenClass); expect(mockElements1[1]).toHaveClass(hiddenClass); @@ -430,8 +390,7 @@ describe('DOM Utilities', () => { try { whenReady(mockCallback); expect(mockCallback).toHaveBeenCalledTimes(1); - } - finally { + } finally { readyStateSpy.mockRestore(); } }); @@ -446,8 +405,7 @@ describe('DOM Utilities', () => { expect(addEventListenerSpy).toHaveBeenCalledTimes(1); expect(addEventListenerSpy).toHaveBeenNthCalledWith(1, 'DOMContentLoaded', mockCallback); expect(mockCallback).not.toHaveBeenCalled(); - } - finally { + } finally { readyStateSpy.mockRestore(); addEventListenerSpy.mockRestore(); } @@ -456,7 +414,9 @@ describe('DOM Utilities', () => { describe('escapeHtml', () => { it('should replace only the expected characters with their HTML entity equivalents', () => { - expect(escapeHtml('')).toBe('<script src="http://example.com/?a=1&b=2"></script>'); + expect(escapeHtml('')).toBe( + '<script src="http://example.com/?a=1&b=2"></script>', + ); }); }); diff --git a/assets/js/utils/__tests__/draggable.spec.ts b/assets/js/utils/__tests__/draggable.spec.ts index cda0598a5..d5415c230 100644 --- a/assets/js/utils/__tests__/draggable.spec.ts +++ b/assets/js/utils/__tests__/draggable.spec.ts @@ -14,7 +14,7 @@ describe('Draggable Utilities', () => { items: items as unknown as DataTransferItemList, setData(format: string, data: string) { items.push({ type: format, getAsString: (callback: FunctionStringCallback) => callback(data) }); - } + }, } as unknown as DataTransfer; } Object.assign(mockEvent, { dataTransfer }); @@ -44,7 +44,6 @@ describe('Draggable Utilities', () => { mockDraggable = createDraggableElement(); mockDragContainer.appendChild(mockDraggable); - // Redirect all document event listeners to this element for easier cleanup documentEventListenerSpy = vi.spyOn(document, 'addEventListener').mockImplementation((...params) => { mockDragContainer.addEventListener(...params); @@ -67,7 +66,7 @@ describe('Draggable Utilities', () => { expect(mockDraggable).toHaveClass(draggingClass); }); - it('should add dummy data to the dragstart event if it\'s empty', () => { + it("should add dummy data to the dragstart event if it's empty", () => { initDraggables(); const mockEvent = createDragEvent('dragstart'); @@ -81,13 +80,13 @@ describe('Draggable Utilities', () => { expect(dataTransferItem.type).toEqual('text/plain'); let stringValue: string | undefined; - dataTransferItem.getAsString(value => { + dataTransferItem.getAsString((value) => { stringValue = value; }); expect(stringValue).toEqual(''); }); - it('should keep data in the dragstart event if it\'s present', () => { + it("should keep data in the dragstart event if it's present", () => { initDraggables(); const mockTransferItemType = getRandomArrayItem(['text/javascript', 'image/jpg', 'application/json']); @@ -95,7 +94,9 @@ describe('Draggable Utilities', () => { type: mockTransferItemType, } as unknown as DataTransferItem; - const mockEvent = createDragEvent('dragstart', { dataTransfer: { items: [mockDataTransferItem] as unknown as DataTransferItemList } } as DragEventInit); + const mockEvent = createDragEvent('dragstart', { + dataTransfer: { items: [mockDataTransferItem] as unknown as DataTransferItemList }, + } as DragEventInit); expect(mockEvent.dataTransfer?.items).toHaveLength(1); fireEvent(mockDraggable, mockEvent); @@ -203,8 +204,7 @@ describe('Draggable Utilities', () => { expect(mockDropEvent.defaultPrevented).toBe(true); expect(mockSecondDraggable).not.toHaveClass(draggingClass); expect(mockSecondDraggable.nextElementSibling).toBe(mockDraggable); - } - finally { + } finally { boundingBoxSpy.mockRestore(); } }); @@ -232,8 +232,7 @@ describe('Draggable Utilities', () => { expect(mockDropEvent.defaultPrevented).toBe(true); expect(mockSecondDraggable).not.toHaveClass(draggingClass); expect(mockDraggable.nextElementSibling).toBe(mockSecondDraggable); - } - finally { + } finally { boundingBoxSpy.mockRestore(); } }); @@ -254,7 +253,7 @@ describe('Draggable Utilities', () => { }); describe('dragEnd', () => { - it('should remove dragging class from source and over class from target\'s descendants', () => { + it("should remove dragging class from source and over class from target's descendants", () => { initDraggables(); const mockStartEvent = createDragEvent('dragstart'); @@ -298,8 +297,7 @@ describe('Draggable Utilities', () => { fireEvent(mockDraggable, mockEvent); expect(mockEvent.dataTransfer?.effectAllowed).toBeFalsy(); - } - finally { + } finally { draggableClosestSpy.mockRestore(); } }); diff --git a/assets/js/utils/__tests__/events.spec.ts b/assets/js/utils/__tests__/events.spec.ts index 575883b7a..8c068e5bb 100644 --- a/assets/js/utils/__tests__/events.spec.ts +++ b/assets/js/utils/__tests__/events.spec.ts @@ -60,7 +60,7 @@ describe('Event utils', () => { const mockButton = document.createElement('button'); const mockHandler = vi.fn(); - mockButton.addEventListener('click', e => leftClick(mockHandler)(e, mockButton)); + mockButton.addEventListener('click', (e) => leftClick(mockHandler)(e, mockButton)); fireEvent.click(mockButton, { button: 0 }); @@ -72,7 +72,7 @@ describe('Event utils', () => { const mockHandler = vi.fn(); const mockButtonNumber = getRandomArrayItem([1, 2, 3, 4, 5]); - mockButton.addEventListener('click', e => leftClick(mockHandler)(e, mockButton)); + mockButton.addEventListener('click', (e) => leftClick(mockHandler)(e, mockButton)); fireEvent.click(mockButton, { button: mockButtonNumber }); diff --git a/assets/js/utils/__tests__/image.spec.ts b/assets/js/utils/__tests__/image.spec.ts index f27a1cd1f..8c3286f5f 100644 --- a/assets/js/utils/__tests__/image.spec.ts +++ b/assets/js/utils/__tests__/image.spec.ts @@ -92,7 +92,7 @@ describe('Image utils', () => { extension: string; videoClasses?: string[]; imgClasses?: string[]; - } + }; const createMockElements = ({ videoClasses, imgClasses, extension }: CreateMockElementsOptions) => { const mockElement = document.createElement('div'); @@ -101,7 +101,7 @@ describe('Image utils', () => { const mockImage = new Image(); mockImage.src = mockImageUri; if (imgClasses) { - imgClasses.forEach(videoClass => { + imgClasses.forEach((videoClass) => { mockImage.classList.add(videoClass); }); } @@ -109,7 +109,7 @@ describe('Image utils', () => { const mockVideo = document.createElement('video'); if (videoClasses) { - videoClasses.forEach(videoClass => { + videoClasses.forEach((videoClass) => { mockVideo.classList.add(videoClass); }); } @@ -131,18 +131,11 @@ describe('Image utils', () => { }; it('should hide the img element and show the video instead if no picture element is present', () => { - const { - mockElement, - mockImage, - playSpy, - mockVideo, - mockSize, - mockSizeUrls, - mockSpoilerOverlay, - } = createMockElements({ - extension: 'webm', - videoClasses: ['hidden'], - }); + const { mockElement, mockImage, playSpy, mockVideo, mockSize, mockSizeUrls, mockSpoilerOverlay } = + createMockElements({ + extension: 'webm', + videoClasses: ['hidden'], + }); const result = showThumb(mockElement); @@ -168,7 +161,7 @@ describe('Image utils', () => { expect(result).toBe(true); }); - ['data-size', 'data-uris'].forEach(missingAttributeName => { + ['data-size', 'data-uris'].forEach((missingAttributeName) => { it(`should return early if the ${missingAttributeName} attribute is missing`, () => { const { mockElement } = createMockElements({ extension: 'webm', @@ -181,8 +174,7 @@ describe('Image utils', () => { const result = showThumb(mockElement); expect(result).toBe(false); expect(jsonParseSpy).not.toHaveBeenCalled(); - } - finally { + } finally { jsonParseSpy.mockRestore(); } }); @@ -226,13 +218,8 @@ describe('Image utils', () => { }); it('should show the correct thumbnail image for jpg extension', () => { - const { - mockElement, - mockSizeImage, - mockSizeUrls, - mockSize, - mockSpoilerOverlay, - } = createMockElementWithPicture('jpg'); + const { mockElement, mockSizeImage, mockSizeUrls, mockSize, mockSpoilerOverlay } = + createMockElementWithPicture('jpg'); const result = showThumb(mockElement); expect(mockSizeImage.src).toBe(mockSizeUrls[mockSize]); @@ -243,13 +230,8 @@ describe('Image utils', () => { }); it('should show the correct thumbnail image for gif extension', () => { - const { - mockElement, - mockSizeImage, - mockSizeUrls, - mockSize, - mockSpoilerOverlay, - } = createMockElementWithPicture('gif'); + const { mockElement, mockSizeImage, mockSizeUrls, mockSize, mockSpoilerOverlay } = + createMockElementWithPicture('gif'); const result = showThumb(mockElement); expect(mockSizeImage.src).toBe(mockSizeUrls[mockSize]); @@ -260,13 +242,8 @@ describe('Image utils', () => { }); it('should show the correct thumbnail image for webm extension', () => { - const { - mockElement, - mockSpoilerOverlay, - mockSizeImage, - mockSizeUrls, - mockSize, - } = createMockElementWithPicture('webm'); + const { mockElement, mockSpoilerOverlay, mockSizeImage, mockSizeUrls, mockSize } = + createMockElementWithPicture('webm'); const result = showThumb(mockElement); expect(mockSizeImage.src).toBe(mockSizeUrls[mockSize].replace('webm', 'gif')); @@ -284,12 +261,10 @@ describe('Image utils', () => { }); const checkSrcsetAttribute = (size: ImageSize, x2size: ImageSize) => { - const { - mockElement, - mockSizeImage, - mockSizeUrls, - mockSpoilerOverlay, - } = createMockElementWithPicture('jpg', size); + const { mockElement, mockSizeImage, mockSizeUrls, mockSpoilerOverlay } = createMockElementWithPicture( + 'jpg', + size, + ); const result = showThumb(mockElement); expect(mockSizeImage.src).toBe(mockSizeUrls[size]); @@ -312,12 +287,10 @@ describe('Image utils', () => { it('should NOT set srcset on img if thumbUri is a gif at small size', () => { const mockSize = 'small'; - const { - mockElement, - mockSizeImage, - mockSizeUrls, - mockSpoilerOverlay, - } = createMockElementWithPicture('gif', mockSize); + const { mockElement, mockSizeImage, mockSizeUrls, mockSpoilerOverlay } = createMockElementWithPicture( + 'gif', + mockSize, + ); const result = showThumb(mockElement); expect(mockSizeImage.src).toBe(mockSizeUrls[mockSize]); @@ -336,12 +309,7 @@ describe('Image utils', () => { }); it('should return false if img source already matches thumbUri', () => { - const { - mockElement, - mockSizeImage, - mockSizeUrls, - mockSize, - } = createMockElementWithPicture('jpg'); + const { mockElement, mockSizeImage, mockSizeUrls, mockSize } = createMockElementWithPicture('jpg'); mockSizeImage.src = mockSizeUrls[mockSize]; const result = showThumb(mockElement); expect(result).toBe(false); @@ -408,8 +376,7 @@ describe('Image utils', () => { expect(querySelectorSpy).toHaveBeenCalledTimes(2); expect(querySelectorSpy).toHaveBeenNthCalledWith(1, 'picture'); expect(querySelectorSpy).toHaveBeenNthCalledWith(2, 'video'); - } - finally { + } finally { querySelectorSpy.mockRestore(); } }); @@ -430,8 +397,7 @@ describe('Image utils', () => { expect(querySelectorSpy).toHaveBeenNthCalledWith(3, 'img'); expect(querySelectorSpy).toHaveBeenNthCalledWith(4, `.${spoilerOverlayClass}`); expect(mockVideo).not.toHaveClass(hiddenClass); - } - finally { + } finally { querySelectorSpy.mockRestore(); pauseSpy.mockRestore(); } @@ -458,8 +424,7 @@ describe('Image utils', () => { expect(mockVideo).toBeEmptyDOMElement(); expect(mockVideo).toHaveClass(hiddenClass); expect(pauseSpy).toHaveBeenCalled(); - } - finally { + } finally { pauseSpy.mockRestore(); } }); @@ -482,8 +447,7 @@ describe('Image utils', () => { expect(imgQuerySelectorSpy).toHaveBeenNthCalledWith(1, 'picture'); expect(pictureQuerySelectorSpy).toHaveBeenNthCalledWith(1, 'img'); expect(imgQuerySelectorSpy).toHaveBeenNthCalledWith(2, `.${spoilerOverlayClass}`); - } - finally { + } finally { imgQuerySelectorSpy.mockRestore(); pictureQuerySelectorSpy.mockRestore(); } diff --git a/assets/js/utils/__tests__/local-autocompleter.spec.ts b/assets/js/utils/__tests__/local-autocompleter.spec.ts index 182e13088..2310c92d2 100644 --- a/assets/js/utils/__tests__/local-autocompleter.spec.ts +++ b/assets/js/utils/__tests__/local-autocompleter.spec.ts @@ -7,7 +7,7 @@ describe('Local Autocompleter', () => { let mockData: ArrayBuffer; const defaultK = 5; - beforeAll(async() => { + beforeAll(async () => { const mockDataPath = join(__dirname, 'autocomplete-compiled-v2.bin'); /** * Read pre-generated binary autocomplete data @@ -78,9 +78,7 @@ describe('Local Autocompleter', () => { it('should return namespaced suggestions without including namespace', () => { const result = localAc.topK('test', defaultK); - expect(result).toEqual([ - expect.objectContaining({ name: 'artist:test', imageCount: 1 }), - ]); + expect(result).toEqual([expect.objectContaining({ name: 'artist:test', imageCount: 1 })]); }); it('should return only the required number of suggestions', () => { diff --git a/assets/js/utils/__tests__/requests.spec.ts b/assets/js/utils/__tests__/requests.spec.ts index fd1af19e7..db8395b97 100644 --- a/assets/js/utils/__tests__/requests.spec.ts +++ b/assets/js/utils/__tests__/requests.spec.ts @@ -29,7 +29,7 @@ describe('Request utils', () => { headers: { 'Content-Type': 'application/json', 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'xmlhttprequest' + 'x-requested-with': 'xmlhttprequest', }, }); }); @@ -46,12 +46,12 @@ describe('Request utils', () => { headers: { 'Content-Type': 'application/json', 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'xmlhttprequest' + 'x-requested-with': 'xmlhttprequest', }, body: JSON.stringify({ ...mockBody, - _method: mockVerb - }) + _method: mockVerb, + }), }); }); }); @@ -64,7 +64,7 @@ describe('Request utils', () => { credentials: 'same-origin', headers: { 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'xmlhttprequest' + 'x-requested-with': 'xmlhttprequest', }, }); }); diff --git a/assets/js/utils/__tests__/store.spec.ts b/assets/js/utils/__tests__/store.spec.ts index b99745bb2..bb8d01685 100644 --- a/assets/js/utils/__tests__/store.spec.ts +++ b/assets/js/utils/__tests__/store.spec.ts @@ -60,9 +60,11 @@ describe('Store utilities', () => { }, }; const initialValueKeys = Object.keys(initialValues) as (keyof typeof initialValues)[]; - setStorageValue(initialValueKeys.reduce((acc, key) => { - return { ...acc, [key]: JSON.stringify(initialValues[key]) }; - }, {})); + setStorageValue( + initialValueKeys.reduce((acc, key) => { + return { ...acc, [key]: JSON.stringify(initialValues[key]) }; + }, {}), + ); initialValueKeys.forEach((key, i) => { const result = store.get(key); @@ -166,7 +168,11 @@ describe('Store utilities', () => { expect(setItemSpy).toHaveBeenCalledTimes(2); expect(setItemSpy).toHaveBeenNthCalledWith(1, mockKey, JSON.stringify(mockValue)); - expect(setItemSpy).toHaveBeenNthCalledWith(2, mockKey + lastUpdatedSuffix, JSON.stringify(initialDateNow + mockMaxAge)); + expect(setItemSpy).toHaveBeenNthCalledWith( + 2, + mockKey + lastUpdatedSuffix, + JSON.stringify(initialDateNow + mockMaxAge), + ); }); }); diff --git a/assets/js/utils/__tests__/tag.spec.ts b/assets/js/utils/__tests__/tag.spec.ts index 61a196b89..3598ee57b 100644 --- a/assets/js/utils/__tests__/tag.spec.ts +++ b/assets/js/utils/__tests__/tag.spec.ts @@ -57,7 +57,7 @@ describe('Tag utilities', () => { }); describe('getHiddenTags', () => { - it('should get a single hidden tag\'s information', () => { + it("should get a single hidden tag's information", () => { window.booru.hiddenTagList = [1, 1]; const result = getHiddenTags(); @@ -72,12 +72,7 @@ describe('Tag utilities', () => { const result = getHiddenTags(); expect(result).toHaveLength(4); - expect(result).toEqual([ - mockTagInfo[3], - mockTagInfo[2], - mockTagInfo[1], - mockTagInfo[4], - ]); + expect(result).toEqual([mockTagInfo[3], mockTagInfo[2], mockTagInfo[1], mockTagInfo[4]]); }); }); @@ -91,7 +86,7 @@ describe('Tag utilities', () => { expect(result).toHaveLength(0); }); - it('should get a single spoilered tag\'s information', () => { + it("should get a single spoilered tag's information", () => { window.booru.spoileredTagList = [1, 1]; window.booru.ignoredTagList = []; window.booru.spoilerType = getEnabledSpoilerType(); @@ -110,12 +105,7 @@ describe('Tag utilities', () => { const result = getSpoileredTags(); expect(result).toHaveLength(4); - expect(result).toEqual([ - mockTagInfo[2], - mockTagInfo[3], - mockTagInfo[1], - mockTagInfo[4], - ]); + expect(result).toEqual([mockTagInfo[2], mockTagInfo[3], mockTagInfo[1], mockTagInfo[4]]); }); it('should omit ignored tags from the list', () => { @@ -125,10 +115,7 @@ describe('Tag utilities', () => { const result = getSpoileredTags(); expect(result).toHaveLength(2); - expect(result).toEqual([ - mockTagInfo[1], - mockTagInfo[4], - ]); + expect(result).toEqual([mockTagInfo[1], mockTagInfo[4]]); }); }); @@ -140,10 +127,7 @@ describe('Tag utilities', () => { const result = imageHitsTags(mockImage, [mockTagInfo[1], mockTagInfo[2], mockTagInfo[3], mockTagInfo[4]]); expect(result).toHaveLength(mockImageTags.length); - expect(result).toEqual([ - mockTagInfo[1], - mockTagInfo[4], - ]); + expect(result).toEqual([mockTagInfo[1], mockTagInfo[4]]); }); it('should return empty array if data attribute is missing', () => { @@ -174,12 +158,16 @@ describe('Tag utilities', () => { it('should return the correct value for two tags', () => { const result = displayTags([mockTagInfo[1], mockTagInfo[4]]); - expect(result).toEqual(`${mockTagInfo[1].name}, ${mockTagInfo[4].name}`); + expect(result).toEqual( + `${mockTagInfo[1].name}, ${mockTagInfo[4].name}`, + ); }); it('should return the correct value for three tags', () => { const result = displayTags([mockTagInfo[1], mockTagInfo[4], mockTagInfo[3]]); - expect(result).toEqual(`${mockTagInfo[1].name}, ${mockTagInfo[4].name}, ${mockTagInfo[3].name}`); + expect(result).toEqual( + `${mockTagInfo[1].name}, ${mockTagInfo[4].name}, ${mockTagInfo[3].name}`, + ); }); it('should escape HTML in the tag name', () => { diff --git a/assets/js/utils/dom.ts b/assets/js/utils/dom.ts index 07705cbe8..12114b9be 100644 --- a/assets/js/utils/dom.ts +++ b/assets/js/utils/dom.ts @@ -5,54 +5,63 @@ type PhilomenaInputElements = HTMLTextAreaElement | HTMLInputElement | HTMLButto /** * Get the first matching element */ -export function $(selector: string, context: Pick = document): E | null { +export function $( + selector: string, + context: Pick = document, +): E | null { return context.querySelector(selector); } /** * Get every matching element as an array */ -export function $$(selector: string, context: Pick = document): E[] { +export function $$( + selector: string, + context: Pick = document, +): E[] { const elements = context.querySelectorAll(selector); return [...elements]; } export function showEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach(el => el.classList.remove('hidden')); + ([] as E[]).concat(...elements).forEach((el) => el.classList.remove('hidden')); } export function hideEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach(el => el.classList.add('hidden')); + ([] as E[]).concat(...elements).forEach((el) => el.classList.add('hidden')); } export function toggleEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach(el => el.classList.toggle('hidden')); + ([] as E[]).concat(...elements).forEach((el) => el.classList.toggle('hidden')); } export function clearEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach(el => { + ([] as E[]).concat(...elements).forEach((el) => { while (el.firstChild) el.removeChild(el.firstChild); }); } export function disableEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach(el => { + ([] as E[]).concat(...elements).forEach((el) => { el.disabled = true; }); } export function enableEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach(el => { + ([] as E[]).concat(...elements).forEach((el) => { el.disabled = false; }); } export function removeEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach(el => el.parentNode?.removeChild(el)); + ([] as E[]).concat(...elements).forEach((el) => el.parentNode?.removeChild(el)); } -export function makeEl(tag: Tag, attr?: Partial): HTMLElementTagNameMap[Tag] { +export function makeEl( + tag: Tag, + attr?: Partial, +): HTMLElementTagNameMap[Tag] { const el = document.createElement(tag); if (attr) { for (const prop in attr) { @@ -65,8 +74,11 @@ export function makeEl(tag: Tag, attr?: return el; } -export function onLeftClick(callback: (e: MouseEvent) => boolean | void, context: Pick = document): VoidFunction { - const handler: typeof callback = event => { +export function onLeftClick( + callback: (e: MouseEvent) => boolean | void, + context: Pick = document, +): VoidFunction { + const handler: typeof callback = (event) => { if (event.button === 0) callback(event); }; context.addEventListener('click', handler); @@ -80,24 +92,19 @@ export function onLeftClick(callback: (e: MouseEvent) => boolean | void, context export function whenReady(callback: VoidFunction): void { if (document.readyState !== 'loading') { callback(); - } - else { + } else { document.addEventListener('DOMContentLoaded', callback); } } export function escapeHtml(html: string): string { - return html.replace(/&/g, '&') - .replace(/>/g, '>') - .replace(//g, '>').replace(/(of: Node): N { - return Array.prototype.filter.call(of.childNodes, el => el.nodeType === Node.TEXT_NODE)[0]; + return Array.prototype.filter.call(of.childNodes, (el) => el.nodeType === Node.TEXT_NODE)[0]; } diff --git a/assets/js/utils/draggable.ts b/assets/js/utils/draggable.ts index 71d0f8bf7..69c5a9d31 100644 --- a/assets/js/utils/draggable.ts +++ b/assets/js/utils/draggable.ts @@ -47,8 +47,7 @@ function drop(event: DragEvent, target: HTMLElement) { if (event.clientX < detX) { target.insertAdjacentElement('beforebegin', dragSrcEl); - } - else { + } else { target.insertAdjacentElement('afterend', dragSrcEl); } } @@ -57,18 +56,18 @@ function dragEnd(event: DragEvent, target: HTMLElement) { clearDragSource(); if (target.parentNode) { - $$('.over', target.parentNode).forEach(t => t.classList.remove('over')); + $$('.over', target.parentNode).forEach((t) => t.classList.remove('over')); } } export function initDraggables() { const draggableSelector = '.drag-container [draggable]'; - delegate(document, 'dragstart', { [draggableSelector]: dragStart}); - delegate(document, 'dragover', { [draggableSelector]: dragOver}); - delegate(document, 'dragenter', { [draggableSelector]: dragEnter}); - delegate(document, 'dragleave', { [draggableSelector]: dragLeave}); - delegate(document, 'dragend', { [draggableSelector]: dragEnd}); - delegate(document, 'drop', { [draggableSelector]: drop}); + delegate(document, 'dragstart', { [draggableSelector]: dragStart }); + delegate(document, 'dragover', { [draggableSelector]: dragOver }); + delegate(document, 'dragenter', { [draggableSelector]: dragEnter }); + delegate(document, 'dragleave', { [draggableSelector]: dragLeave }); + delegate(document, 'dragend', { [draggableSelector]: dragEnd }); + delegate(document, 'drop', { [draggableSelector]: drop }); } export function clearDragSource() { diff --git a/assets/js/utils/events.ts b/assets/js/utils/events.ts index e92b0109a..06733721e 100644 --- a/assets/js/utils/events.ts +++ b/assets/js/utils/events.ts @@ -3,16 +3,16 @@ import '../../types/ujs'; export interface PhilomenaAvailableEventsMap { - dragstart: DragEvent, - dragover: DragEvent, - dragenter: DragEvent, - dragleave: DragEvent, - dragend: DragEvent, - drop: DragEvent, - click: MouseEvent, - submit: Event, - reset: Event, - fetchcomplete: FetchcompleteEvent + dragstart: DragEvent; + dragover: DragEvent; + dragenter: DragEvent; + dragleave: DragEvent; + dragend: DragEvent; + drop: DragEvent; + click: MouseEvent; + submit: Event; + reset: Event; + fetchcomplete: FetchcompleteEvent; } export interface PhilomenaEventElement { @@ -20,7 +20,7 @@ export interface PhilomenaEventElement { type: K, // eslint-disable-next-line @typescript-eslint/no-explicit-any listener: (this: Document | HTMLElement, ev: PhilomenaAvailableEventsMap[K]) => any, - options?: boolean | AddEventListenerOptions | undefined + options?: boolean | AddEventListenerOptions | undefined, ): void; } @@ -30,21 +30,25 @@ export function fire(el: El, event: string, detail: D) { export function on( node: PhilomenaEventElement, - event: K, selector: string, func: ((e: PhilomenaAvailableEventsMap[K], target: Element) => boolean) + event: K, + selector: string, + func: (e: PhilomenaAvailableEventsMap[K], target: Element) => boolean, ) { delegate(node, event, { [selector]: func }); } export function leftClick(func: (e: E, t: Target) => void) { - return (event: E, target: Target) => { if (event.button === 0) return func(event, target); }; + return (event: E, target: Target) => { + if (event.button === 0) return func(event, target); + }; } export function delegate( node: PhilomenaEventElement, event: K, - selectors: Record void | boolean)> + selectors: Record void | boolean>, ) { - node.addEventListener(event, e => { + node.addEventListener(event, (e) => { for (const selector in selectors) { const evtTarget = e.target as EventTarget | Target | null; if (evtTarget && 'closest' in evtTarget && typeof evtTarget.closest === 'function') { diff --git a/assets/js/utils/image.ts b/assets/js/utils/image.ts index 99ab27220..cbdbb20e6 100644 --- a/assets/js/utils/image.ts +++ b/assets/js/utils/image.ts @@ -53,8 +53,7 @@ export function showThumb(img: HTMLDivElement) { if (uris[size].indexOf('.webm') !== -1) { overlay.classList.remove('hidden'); overlay.innerHTML = 'WebM'; - } - else { + } else { overlay.classList.add('hidden'); } @@ -118,7 +117,9 @@ export function spoilerThumb(img: HTMLDivElement, spoilerUri: string, reason: st switch (window.booru.spoilerType) { case 'click': - img.addEventListener('click', event => { if (showThumb(img)) event.preventDefault(); }); + img.addEventListener('click', (event) => { + if (showThumb(img)) event.preventDefault(); + }); img.addEventListener('mouseleave', () => hideThumb(img, spoilerUri, reason)); break; case 'hover': diff --git a/assets/js/utils/lerp.ts b/assets/js/utils/lerp.ts index 41170284f..42d58c2e5 100644 --- a/assets/js/utils/lerp.ts +++ b/assets/js/utils/lerp.ts @@ -5,8 +5,11 @@ // clamp the value between min and max, depending on whether // the delta >= 1 or <= 0. export function lerp(delta: number, from: number, to: number): number { - if (delta >= 1) { return to; } - else if (delta <= 0) { return from; } + if (delta >= 1) { + return to; + } else if (delta <= 0) { + return from; + } return from + (to - from) * delta; } diff --git a/assets/js/utils/local-autocompleter.ts b/assets/js/utils/local-autocompleter.ts index 73d88f921..f27f8e2dd 100644 --- a/assets/js/utils/local-autocompleter.ts +++ b/assets/js/utils/local-autocompleter.ts @@ -70,7 +70,7 @@ export class LocalAutocompleter { associations.push(this.view.getUint32(location + 1 + nameLength + 1 + i * 4, true)); } - return [ name, associations ]; + return [name, associations]; } /** @@ -79,14 +79,14 @@ export class LocalAutocompleter { getResultAt(i: number): [string, Result] { const nameLocation = this.view.getUint32(this.referenceStart + i * 8, true); const imageCount = this.view.getInt32(this.referenceStart + i * 8 + 4, true); - const [ name, associations ] = this.getTagFromLocation(nameLocation); + const [name, associations] = this.getTagFromLocation(nameLocation); if (imageCount < 0) { // This is actually an alias, so follow it - return [ name, this.getResultAt(-imageCount - 1)[1] ]; + return [name, this.getResultAt(-imageCount - 1)[1]]; } - return [ name, { name, imageCount, associations } ]; + return [name, { name, imageCount, associations }]; } /** @@ -100,7 +100,11 @@ export class LocalAutocompleter { /** * Perform a binary search to fetch all results matching a condition. */ - scanResults(getResult: (i: number) => [string, Result], compare: (name: string) => number, results: Record) { + scanResults( + getResult: (i: number) => [string, Result], + compare: (name: string) => number, + results: Record, + ) { const unfilter = store.get('unfilter_tag_suggestions'); let min = 0; @@ -109,14 +113,13 @@ export class LocalAutocompleter { const hiddenTags = window.booru.hiddenTagList; while (min < max - 1) { - const med = min + (max - min) / 2 | 0; + const med = (min + (max - min) / 2) | 0; const sortKey = getResult(med)[0]; if (compare(sortKey) >= 0) { // too large, go left max = med; - } - else { + } else { // too small, go right min = med; } @@ -124,13 +127,13 @@ export class LocalAutocompleter { // Scan forward until no more matches occur while (min < this.numTags - 1) { - const [ sortKey, result ] = getResult(++min); + const [sortKey, result] = getResult(++min); if (compare(sortKey) !== 0) { break; } // Add if not filtering or no associations are filtered - if (unfilter || hiddenTags.findIndex(ht => result.associations.includes(ht)) === -1) { + if (unfilter || hiddenTags.findIndex((ht) => result.associations.includes(ht)) === -1) { results[result.name] = result; } } diff --git a/assets/js/utils/requests.ts b/assets/js/utils/requests.ts index e71a76625..33a045a4f 100644 --- a/assets/js/utils/requests.ts +++ b/assets/js/utils/requests.ts @@ -9,7 +9,7 @@ export function fetchJson(verb: HttpMethod, endpoint: string, body?: Record { credentials: 'same-origin', headers: { 'x-csrf-token': window.booru.csrfToken, - 'x-requested-with': 'xmlhttprequest' + 'x-requested-with': 'xmlhttprequest', }, }); } diff --git a/assets/js/utils/store.ts b/assets/js/utils/store.ts index a71d4256b..d4b8579d1 100644 --- a/assets/js/utils/store.ts +++ b/assets/js/utils/store.ts @@ -5,13 +5,11 @@ export const lastUpdatedSuffix = '__lastUpdated'; export default { - set(key: string, value: unknown) { try { localStorage.setItem(key, JSON.stringify(value)); return true; - } - catch { + } catch { return false; } }, @@ -21,8 +19,7 @@ export default { if (value === null) return null; try { return JSON.parse(value); - } - catch { + } catch { return value as unknown as Value; } }, @@ -31,8 +28,7 @@ export default { try { localStorage.removeItem(key); return true; - } - catch { + } catch { return false; } }, @@ -61,5 +57,4 @@ export default { return lastUpdatedTime === null || Date.now() > lastUpdatedTime; }, - }; diff --git a/assets/js/utils/tag.ts b/assets/js/utils/tag.ts index 26c6bdf98..7132efcb6 100644 --- a/assets/js/utils/tag.ts +++ b/assets/js/utils/tag.ts @@ -30,7 +30,7 @@ function sortTags(hidden: boolean, a: TagData, b: TagData): number { export function getHiddenTags(): TagData[] { return unique(window.booru.hiddenTagList) - .map(tagId => getTag(tagId)) + .map((tagId) => getTag(tagId)) .sort(sortTags.bind(null, true)); } @@ -38,8 +38,8 @@ export function getSpoileredTags(): TagData[] { if (window.booru.spoilerType === 'off') return []; return unique(window.booru.spoileredTagList) - .filter(tagId => window.booru.ignoredTagList.indexOf(tagId) === -1) - .map(tagId => getTag(tagId)) + .filter((tagId) => window.booru.ignoredTagList.indexOf(tagId) === -1) + .map((tagId) => getTag(tagId)) .sort(sortTags.bind(null, false)); } @@ -49,7 +49,7 @@ export function imageHitsTags(img: HTMLElement, matchTags: TagData[]): TagData[] return []; } const imageTags = JSON.parse(imageTagsString); - return matchTags.filter(t => imageTags.indexOf(t.id) !== -1); + return matchTags.filter((t) => imageTags.indexOf(t.id) !== -1); } export function imageHitsComplex(img: HTMLElement, matchComplex: AstMatcher) { @@ -57,11 +57,13 @@ export function imageHitsComplex(img: HTMLElement, matchComplex: AstMatcher) { } export function displayTags(tags: TagData[]): string { - const mainTag = tags[0], otherTags = tags.slice(1); - let list = escapeHtml(mainTag.name), extras; + const mainTag = tags[0], + otherTags = tags.slice(1); + let list = escapeHtml(mainTag.name), + extras; if (otherTags.length > 0) { - extras = otherTags.map(tag => escapeHtml(tag.name)).join(', '); + extras = otherTags.map((tag) => escapeHtml(tag.name)).join(', '); list += `, ${extras}`; } diff --git a/assets/js/when-ready.ts b/assets/js/when-ready.ts index 07462efbb..08d34edbc 100644 --- a/assets/js/when-ready.ts +++ b/assets/js/when-ready.ts @@ -2,41 +2,40 @@ * Functions to execute when the DOM is ready */ -import { whenReady } from './utils/dom'; +import { whenReady } from './utils/dom'; -import { listenAutocomplete } from './autocomplete'; -import { loadBooruData } from './booru'; -import { registerEvents } from './boorujs'; -import { setupBurgerMenu } from './burger'; -import { bindCaptchaLinks } from './captcha'; -import { setupComments } from './comment'; -import { setupDupeReports } from './duplicate_reports'; -import { setSesCookie } from './fp'; -import { setupGalleryEditing } from './galleries'; +import { listenAutocomplete } from './autocomplete'; +import { loadBooruData } from './booru'; +import { registerEvents } from './boorujs'; +import { setupBurgerMenu } from './burger'; +import { bindCaptchaLinks } from './captcha'; +import { setupComments } from './comment'; +import { setupDupeReports } from './duplicate_reports'; +import { setSesCookie } from './fp'; +import { setupGalleryEditing } from './galleries'; import { initImagesClientside } from './imagesclientside'; -import { bindImageTarget } from './image_expansion'; -import { setupEvents } from './misc'; -import { setupNotifications } from './notifications'; -import { setupPreviews } from './preview'; -import { setupQuickTag } from './quick-tag'; -import { setupSettings } from './settings'; -import { listenForKeys } from './shortcuts'; -import { initTagDropdown } from './tags'; -import { setupTagListener } from './tagsinput'; -import { setupTagEvents } from './tagsmisc'; -import { setupTimestamps } from './timeago'; -import { setupImageUpload } from './upload'; -import { setupSearch } from './search'; -import { setupToolbar } from './markdowntoolbar'; -import { hideStaffTools } from './staffhider'; -import { pollOptionCreator } from './poll'; -import { warnAboutPMs } from './pmwarning'; -import { imageSourcesCreator } from './sources'; -import { sizeGraphs } from './graph'; -import { setupSliders } from './slider'; +import { bindImageTarget } from './image_expansion'; +import { setupEvents } from './misc'; +import { setupNotifications } from './notifications'; +import { setupPreviews } from './preview'; +import { setupQuickTag } from './quick-tag'; +import { setupSettings } from './settings'; +import { listenForKeys } from './shortcuts'; +import { initTagDropdown } from './tags'; +import { setupTagListener } from './tagsinput'; +import { setupTagEvents } from './tagsmisc'; +import { setupTimestamps } from './timeago'; +import { setupImageUpload } from './upload'; +import { setupSearch } from './search'; +import { setupToolbar } from './markdowntoolbar'; +import { hideStaffTools } from './staffhider'; +import { pollOptionCreator } from './poll'; +import { warnAboutPMs } from './pmwarning'; +import { imageSourcesCreator } from './sources'; +import { sizeGraphs } from './graph'; +import { setupSliders } from './slider'; whenReady(() => { - loadBooruData(); listenAutocomplete(); registerEvents(); @@ -67,5 +66,4 @@ whenReady(() => { imageSourcesCreator(); setupSliders(); sizeGraphs(); - }); diff --git a/assets/package-lock.json b/assets/package-lock.json index 95dcdbf55..a16baf7cc 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -11,13 +11,10 @@ "@types/web": "^0.0.148", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", - "eslint": "^9.4.0", - "jest-environment-jsdom": "^29.7.0", "normalize.css": "^8.0.1", "postcss-mixins": "^10.0.1", "postcss-simple-vars": "^7.0.1", "typescript": "^5.4", - "typescript-eslint": "8.0.0-alpha.39", "vite": "^5.2" }, "devDependencies": { @@ -26,12 +23,17 @@ "@types/chai-dom": "^1.11.3", "@vitest/coverage-v8": "^1.6.0", "chai": "^5", + "eslint": "^9.4.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-vitest": "^0.5.4", + "jest-environment-jsdom": "^29.7.0", "jsdom": "^24.1.0", "prettier": "^3.3.2", "stylelint": "^16.6.1", "stylelint-config-standard": "^36.0.0", "stylelint-prettier": "^5.0.0", + "typescript-eslint": "8.0.0-alpha.39", "vitest": "^1.6.0", "vitest-fetch-mock": "^0.2.2" } @@ -59,6 +61,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, "dependencies": { "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" @@ -80,6 +83,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -88,6 +92,7 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", @@ -102,6 +107,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -113,6 +119,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -126,6 +133,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -133,12 +141,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -147,6 +157,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -155,6 +166,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -788,6 +800,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -802,6 +815,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -813,6 +827,7 @@ "version": "4.11.0", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -821,6 +836,7 @@ "version": "0.17.0", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz", "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==", + "dev": true, "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -834,6 +850,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -856,6 +873,7 @@ "version": "9.6.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -864,6 +882,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -881,6 +900,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "engines": { "node": ">=12.22" }, @@ -893,6 +913,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, "engines": { "node": ">=18.18" }, @@ -914,6 +935,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -928,6 +950,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -944,6 +967,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -955,6 +979,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -1047,6 +1072,18 @@ "node": ">= 8" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", @@ -1242,12 +1279,14 @@ "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, "dependencies": { "type-detect": "4.0.8" } @@ -1256,6 +1295,7 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0" } @@ -1347,6 +1387,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, "engines": { "node": ">= 10" } @@ -1380,12 +1421,14 @@ "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -1394,6 +1437,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } @@ -1402,6 +1446,7 @@ "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", @@ -1412,6 +1457,7 @@ "version": "20.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "devOptional": true, "dependencies": { "undici-types": "~5.26.4" } @@ -1427,12 +1473,14 @@ "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true }, "node_modules/@types/web": { "version": "0.0.148", @@ -1443,6 +1491,7 @@ "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, "dependencies": { "@types/yargs-parser": "*" } @@ -1450,12 +1499,14 @@ "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.39.tgz", "integrity": "sha512-ILv1vDA8M9ah1vzYpnOs4UOLRdB63Ki/rsxedVikjMLq68hFfpsDR25bdMZ4RyUkzLJwOhcg3Jujm/C1nupXKA==", + "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.0.0-alpha.39", @@ -1488,6 +1539,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.39.tgz", "integrity": "sha512-5k+pwV91plJojHgZkWlq4/TQdOrnEaeSvt48V0m8iEwdMJqX/63BXYxy8BUOSghWcjp05s73vy9HJjovAKmHkQ==", + "dev": true, "dependencies": { "@typescript-eslint/scope-manager": "8.0.0-alpha.39", "@typescript-eslint/types": "8.0.0-alpha.39", @@ -1515,6 +1567,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.39.tgz", "integrity": "sha512-HCBlKQROY+JIgWolucdFMj1W3VUnnIQTdxAhxJTAj3ix2nASmvKIFgrdo5KQMrXxQj6tC4l3zva10L+s0dUIIw==", + "dev": true, "dependencies": { "@typescript-eslint/types": "8.0.0-alpha.39", "@typescript-eslint/visitor-keys": "8.0.0-alpha.39" @@ -1531,6 +1584,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.39.tgz", "integrity": "sha512-alO13fRU6yVeJbwl9ESI3AYhq5dQdz3Dpd0I5B4uezs2lvgYp44dZsj5hWyPz/kL7JFEsjbn+4b/CZA0OQJzjA==", + "dev": true, "dependencies": { "@typescript-eslint/typescript-estree": "8.0.0-alpha.39", "@typescript-eslint/utils": "8.0.0-alpha.39", @@ -1554,6 +1608,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.39.tgz", "integrity": "sha512-yINN7j0/+S1VGSp0IgH52oQvUx49vkOug6xbrDA/9o+U55yCAQKSvYWvzYjNa+SZE3hXI0zwvYtMVsIAAMmKIQ==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1566,6 +1621,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.39.tgz", "integrity": "sha512-S8gREuP8r8PCxGegeojeXntx0P50ul9YH7c7JYpbLIIsEPNr5f7UHlm+I1NUbL04CBin4kvZ60TG4eWr/KKN9A==", + "dev": true, "dependencies": { "@typescript-eslint/types": "8.0.0-alpha.39", "@typescript-eslint/visitor-keys": "8.0.0-alpha.39", @@ -1593,6 +1649,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -1601,6 +1658,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1615,6 +1673,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.39.tgz", "integrity": "sha512-Nr2PrlfNhrNQTlFHlD7XJdTGw/Vt8qY44irk6bfjn9LxGdSG5e4c1R2UN6kvGMhhx20DBPbM7q3Z3r+huzmL1w==", + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.0.0-alpha.39", @@ -1636,6 +1695,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.39.tgz", "integrity": "sha512-DVJ0UdhucZy+/1GlIy7FX2+CFhCeNAi4VwaEAe7u2UDenQr9/kGqvzx00UlpWibmEVDw4KsPOI7Aqa1+2Vqfmw==", + "dev": true, "dependencies": { "@typescript-eslint/types": "8.0.0-alpha.39", "eslint-visitor-keys": "^3.4.3" @@ -1652,6 +1712,7 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1928,12 +1989,14 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead" + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1945,6 +2008,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, "dependencies": { "acorn": "^8.1.0", "acorn-walk": "^8.0.2" @@ -1954,6 +2018,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -1962,6 +2027,7 @@ "version": "8.3.3", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, "dependencies": { "acorn": "^8.11.0" }, @@ -1985,6 +2051,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2000,6 +2067,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2008,6 +2076,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -2021,7 +2090,8 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/aria-query": { "version": "5.3.0", @@ -2036,6 +2106,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { "node": ">=8" } @@ -2061,7 +2132,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/autoprefixer": { "version": "10.4.19", @@ -2102,12 +2174,14 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2168,6 +2242,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -2219,6 +2294,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2243,6 +2319,7 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, "funding": [ { "type": "github", @@ -2257,6 +2334,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -2267,7 +2345,8 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/colord": { "version": "2.9.3", @@ -2279,6 +2358,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2289,7 +2369,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/confbox": { "version": "0.1.7", @@ -2405,7 +2486,8 @@ "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true }, "node_modules/cssstyle": { "version": "4.0.1", @@ -2442,6 +2524,7 @@ "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -2457,7 +2540,8 @@ "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true }, "node_modules/deep-eql": { "version": "5.0.2", @@ -2471,12 +2555,14 @@ "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -2503,6 +2589,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, "dependencies": { "path-type": "^4.0.0" }, @@ -2521,6 +2608,7 @@ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", "deprecated": "Use your platform's native DOMException instead", + "dev": true, "dependencies": { "webidl-conversions": "^7.0.0" }, @@ -2543,6 +2631,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "engines": { "node": ">=0.12" }, @@ -2617,6 +2706,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { "node": ">=10" }, @@ -2628,6 +2718,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -2648,6 +2739,7 @@ "version": "9.6.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.6.0.tgz", "integrity": "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==", + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2694,6 +2786,48 @@ "url": "https://eslint.org/donate" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-vitest": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", @@ -2855,6 +2989,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -2870,6 +3005,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2881,6 +3017,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dev": true, "dependencies": { "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", @@ -2897,6 +3034,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2909,6 +3047,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, "dependencies": { "estraverse": "^5.1.0" }, @@ -2920,6 +3059,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -2931,6 +3071,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { "node": ">=4.0" } @@ -2948,6 +3089,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2978,7 +3120,8 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-diff": { "version": "1.3.0", @@ -3015,12 +3158,14 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fastest-levenshtein": { "version": "1.0.16", @@ -3043,6 +3188,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -3065,6 +3211,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -3080,6 +3227,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -3091,12 +3239,14 @@ "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -3183,6 +3333,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -3232,6 +3383,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, "engines": { "node": ">=18" }, @@ -3243,6 +3395,7 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -3267,17 +3420,20 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -3351,6 +3507,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -3362,6 +3519,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, "engines": { "node": ">= 4" } @@ -3370,6 +3528,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3385,6 +3544,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "engines": { "node": ">=0.8.19" } @@ -3467,6 +3627,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -3483,7 +3644,8 @@ "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true }, "node_modules/is-stream": { "version": "3.0.0", @@ -3556,6 +3718,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -3582,6 +3745,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, "dependencies": { "debug": "4" }, @@ -3593,6 +3757,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, "dependencies": { "cssom": "~0.3.6" }, @@ -3603,12 +3768,14 @@ "node_modules/jest-environment-jsdom/node_modules/cssstyle/node_modules/cssom": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true }, "node_modules/jest-environment-jsdom/node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, "dependencies": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", @@ -3622,6 +3789,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, "dependencies": { "whatwg-encoding": "^2.0.0" }, @@ -3633,6 +3801,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -3646,6 +3815,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -3658,6 +3828,7 @@ "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, "dependencies": { "abab": "^2.0.6", "acorn": "^8.8.1", @@ -3702,6 +3873,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, "dependencies": { "punycode": "^2.1.1" }, @@ -3713,6 +3885,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, "dependencies": { "xml-name-validator": "^4.0.0" }, @@ -3724,6 +3897,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -3735,6 +3909,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, "engines": { "node": ">=12" } @@ -3743,6 +3918,7 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" @@ -3755,6 +3931,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, "engines": { "node": ">=12" } @@ -3763,6 +3940,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", @@ -3782,6 +3960,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, "engines": { "node": ">=10" }, @@ -3793,6 +3972,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -3805,12 +3985,14 @@ "node_modules/jest-message-util/node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true }, "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -3824,6 +4006,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -3839,12 +4022,14 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -3895,7 +4080,8 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -3906,17 +4092,20 @@ "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -3940,6 +4129,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -3974,6 +4164,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -3993,7 +4184,8 @@ "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true }, "node_modules/lodash.truncate": { "version": "4.4.2", @@ -4112,6 +4304,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "engines": { "node": ">= 0.6" } @@ -4120,6 +4313,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -4152,6 +4346,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4174,7 +4369,8 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/nanoid": { "version": "3.3.7", @@ -4196,7 +4392,8 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, "node_modules/node-fetch": { "version": "2.7.0", @@ -4297,7 +4494,8 @@ "node_modules/nwsapi": { "version": "2.2.10", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", - "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==" + "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", + "dev": true }, "node_modules/once": { "version": "1.4.0", @@ -4327,6 +4525,7 @@ "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -4343,6 +4542,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -4357,6 +4557,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -4371,6 +4572,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4400,6 +4602,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, "dependencies": { "entities": "^4.4.0" }, @@ -4411,6 +4614,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { "node": ">=8" } @@ -4436,6 +4640,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -4623,6 +4828,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "engines": { "node": ">= 0.8.0" } @@ -4683,12 +4889,14 @@ "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "engines": { "node": ">=6" } @@ -4696,7 +4904,8 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -4754,12 +4963,14 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -4838,12 +5049,14 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, "dependencies": { "xmlchars": "^2.2.0" }, @@ -4855,6 +5068,7 @@ "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, "bin": { "semver": "bin/semver.js" }, @@ -4903,6 +5117,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { "node": ">=8" } @@ -4928,6 +5143,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "optional": true, "engines": { "node": ">=0.10.0" @@ -4945,6 +5161,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -4956,6 +5173,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, "engines": { "node": ">=8" } @@ -4990,6 +5208,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5025,6 +5244,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" }, @@ -5262,6 +5482,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5291,7 +5512,24 @@ "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } }, "node_modules/table": { "version": "6.8.2", @@ -5348,7 +5586,8 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/tinybench": { "version": "2.8.0", @@ -5398,6 +5637,7 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -5424,6 +5664,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, "engines": { "node": ">=16" }, @@ -5431,10 +5672,17 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -5446,6 +5694,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, "engines": { "node": ">=4" } @@ -5466,6 +5715,7 @@ "version": "8.0.0-alpha.39", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.39.tgz", "integrity": "sha512-bsuR1BVJfHr7sBh7Cca962VPIcP+5UWaIa/+6PpnFZ+qtASjGTxKWIF5dG2o73BX9NsyqQfvRWujb3M9CIoRXA==", + "dev": true, "dependencies": { "@typescript-eslint/eslint-plugin": "8.0.0-alpha.39", "@typescript-eslint/parser": "8.0.0-alpha.39", @@ -5493,12 +5743,14 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true }, "node_modules/universalify": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, "engines": { "node": ">= 4.0.0" } @@ -5536,6 +5788,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -5544,6 +5797,7 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -5796,6 +6050,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, "engines": { "node": ">=12" } @@ -5868,6 +6123,7 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -5895,6 +6151,7 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, "engines": { "node": ">=10.0.0" }, @@ -5923,12 +6180,14 @@ "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "engines": { "node": ">=10" }, diff --git a/assets/package.json b/assets/package.json index 8fe6f2074..1dbf43991 100644 --- a/assets/package.json +++ b/assets/package.json @@ -14,11 +14,8 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@types/postcss-mixins": "^9.0.5", "@types/web": "^0.0.148", - "typescript-eslint": "8.0.0-alpha.39", "autoprefixer": "^10.4.19", "cross-env": "^7.0.3", - "eslint": "^9.4.0", - "jest-environment-jsdom": "^29.7.0", "normalize.css": "^8.0.1", "postcss-mixins": "^10.0.1", "postcss-simple-vars": "^7.0.1", @@ -31,12 +28,16 @@ "@types/chai-dom": "^1.11.3", "@vitest/coverage-v8": "^1.6.0", "chai": "^5", + "eslint": "^9.4.0", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-vitest": "^0.5.4", + "jest-environment-jsdom": "^29.7.0", "jsdom": "^24.1.0", "prettier": "^3.3.2", "stylelint": "^16.6.1", "stylelint-config-standard": "^36.0.0", "stylelint-prettier": "^5.0.0", + "typescript-eslint": "8.0.0-alpha.39", "vitest": "^1.6.0", "vitest-fetch-mock": "^0.2.2" } diff --git a/assets/test/fix-event-listeners.ts b/assets/test/fix-event-listeners.ts index d4e0a8bff..7a4b07e5e 100644 --- a/assets/test/fix-event-listeners.ts +++ b/assets/test/fix-event-listeners.ts @@ -8,7 +8,7 @@ export function fixEventListeners(t: EventTarget) { eventListeners = {}; const oldAddEventListener = t.addEventListener; - t.addEventListener = function(type: string, listener: any, options: any): void { + t.addEventListener = (type: string, listener: any, options: any): void => { eventListeners[type] = eventListeners[type] || []; eventListeners[type].push(listener); return oldAddEventListener(type, listener, options); diff --git a/assets/test/mock-storage.ts b/assets/test/mock-storage.ts index d7b337a9c..c05bcda6d 100644 --- a/assets/test/mock-storage.ts +++ b/assets/test/mock-storage.ts @@ -2,7 +2,9 @@ import { MockInstance } from 'vitest'; type MockStorageKeys = 'getItem' | 'setItem' | 'removeItem'; -export function mockStorage(options: Pick): { [k in `${Keys}Spy`]: MockInstance } { +export function mockStorage( + options: Pick, +): { [k in `${Keys}Spy`]: MockInstance } { const getItemSpy = 'getItem' in options ? vi.spyOn(Storage.prototype, 'getItem') : undefined; const setItemSpy = 'setItem' in options ? vi.spyOn(Storage.prototype, 'setItem') : undefined; const removeItemSpy = 'removeItem' in options ? vi.spyOn(Storage.prototype, 'removeItem') : undefined; @@ -33,18 +35,18 @@ type MockStorageImplApi = { [k in `${MockStorageKeys}Spy`]: MockInstance } & { * Forces the mock storage back to its default (empty) state * @param value */ - clearStorage: VoidFunction, + clearStorage: VoidFunction; /** * Forces the mock storage to be in the specific state provided as the parameter * @param value */ - setStorageValue: (value: Record) => void, + setStorageValue: (value: Record) => void; /** * Forces the mock storage to throw an error for the duration of the provided function's execution, * or in case a promise is returned by the function, until that promise is resolved. */ - forceStorageError: (func: (...args: Args[]) => Return | Promise) => void -} + forceStorageError: (func: (...args: Args[]) => Return | Promise) => void; +}; export function mockStorageImpl(): MockStorageImplApi { let shouldThrow = false; @@ -66,7 +68,7 @@ export function mockStorageImpl(): MockStorageImplApi { delete tempStorage[key]; }, }); - const forceStorageError: MockStorageImplApi['forceStorageError'] = func => { + const forceStorageError: MockStorageImplApi['forceStorageError'] = (func) => { shouldThrow = true; const value = func(); if (!(value instanceof Promise)) { @@ -78,7 +80,7 @@ export function mockStorageImpl(): MockStorageImplApi { shouldThrow = false; }); }; - const setStorageValue: MockStorageImplApi['setStorageValue'] = value => { + const setStorageValue: MockStorageImplApi['setStorageValue'] = (value) => { tempStorage = value; }; const clearStorage = () => setStorageValue({}); diff --git a/assets/test/vitest-setup.ts b/assets/test/vitest-setup.ts index f6de62b6a..ae3a62f8c 100644 --- a/assets/test/vitest-setup.ts +++ b/assets/test/vitest-setup.ts @@ -21,7 +21,7 @@ window.booru = { spoileredFilter: matchNone(), interactions: [], tagsVersion: 5, - galleryImages: [] + galleryImages: [], }; // https://github.com/jsdom/jsdom/issues/1721#issuecomment-1484202038 @@ -31,6 +31,7 @@ Object.assign(globalThis, { URL, Blob }); // Prevents an error when calling `form.submit()` directly in // the code that is being tested +// eslint-disable-next-line prettier/prettier HTMLFormElement.prototype.submit = function() { fireEvent.submit(this); }; diff --git a/assets/types/ujs.ts b/assets/types/ujs.ts index f9cb88f5a..5853216d0 100644 --- a/assets/types/ujs.ts +++ b/assets/types/ujs.ts @@ -2,7 +2,7 @@ export {}; declare global { interface FetchcompleteEvent extends CustomEvent { - target: HTMLElement, + target: HTMLElement; } interface GlobalEventHandlersEventMap { diff --git a/assets/vite.config.ts b/assets/vite.config.ts index 9342c8895..2eb4607d1 100644 --- a/assets/vite.config.ts +++ b/assets/vite.config.ts @@ -11,18 +11,16 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { const isDev = command !== 'build' && mode !== 'test'; const targets = new Map(); - fs.readdirSync(path.resolve(__dirname, 'css/themes/')).forEach(name => { + fs.readdirSync(path.resolve(__dirname, 'css/themes/')).forEach((name) => { const m = name.match(/([-a-z]+).css/); - if (m) - targets.set(`css/${m[1]}`, `./css/themes/${m[1]}.css`); + if (m) targets.set(`css/${m[1]}`, `./css/themes/${m[1]}.css`); }); - fs.readdirSync(path.resolve(__dirname, 'css/options/')).forEach(name => { + fs.readdirSync(path.resolve(__dirname, 'css/options/')).forEach((name) => { const m = name.match(/([-a-z]+).css/); - if (m) - targets.set(`css/options/${m[1]}`, `./css/options/${m[1]}.css`); + if (m) targets.set(`css/options/${m[1]}`, `./css/options/${m[1]}.css`); }); return { @@ -37,8 +35,8 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { common: path.resolve(__dirname, 'css/common/'), views: path.resolve(__dirname, 'css/views/'), elements: path.resolve(__dirname, 'css/elements/'), - themes: path.resolve(__dirname, 'css/themes/') - } + themes: path.resolve(__dirname, 'css/themes/'), + }, }, build: { target: ['es2016', 'chrome67', 'firefox62', 'edge18', 'safari12'], @@ -51,19 +49,19 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { input: { 'js/app': './js/app.ts', 'css/application': './css/application.css', - ...Object.fromEntries(targets) + ...Object.fromEntries(targets), }, output: { entryFileNames: '[name].js', chunkFileNames: '[name].js', - assetFileNames: '[name][extname]' - } - } + assetFileNames: '[name][extname]', + }, + }, }, css: { - postcss: { - plugins: [postcssMixins(), postcssSimpleVars(), postcssRelativeColor(), autoprefixer] - } + postcss: { + plugins: [postcssMixins(), postcssSimpleVars(), postcssRelativeColor(), autoprefixer], + }, }, test: { globals: true, @@ -74,11 +72,7 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { coverage: { reporter: ['text', 'html'], include: ['js/**/*.{js,ts}'], - exclude: [ - 'node_modules/', - '.*\\.test\\.ts$', - '.*\\.d\\.ts$', - ], + exclude: ['node_modules/', '.*\\.test\\.ts$', '.*\\.d\\.ts$'], thresholds: { statements: 0, branches: 0, @@ -90,8 +84,8 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { functions: 100, lines: 100, }, - } - } - } + }, + }, + }, }; }); From fbd18fd1fdb936ac60fe828c63315be84aac6585 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Wed, 3 Jul 2024 23:03:46 +0200 Subject: [PATCH 63/65] arrowParens: avoid --- assets/.prettierrc.yml | 2 +- assets/js/__tests__/ujs.spec.ts | 16 ++++---- assets/js/__tests__/upload.spec.ts | 6 +-- assets/js/autocomplete.js | 14 +++---- assets/js/booru.js | 8 ++-- assets/js/boorujs.js | 18 ++++----- assets/js/burger.ts | 4 +- assets/js/comment.js | 12 +++--- assets/js/fp.ts | 4 +- assets/js/galleries.ts | 4 +- assets/js/graph.ts | 4 +- assets/js/image_expansion.js | 2 +- assets/js/imagesclientside.ts | 12 +++--- assets/js/input-duplicator.ts | 4 +- assets/js/interactions.js | 44 ++++++++++----------- assets/js/markdowntoolbar.js | 12 +++--- assets/js/misc.ts | 6 +-- assets/js/notifications.ts | 6 +-- assets/js/preview.js | 4 +- assets/js/query/boolean.ts | 4 +- assets/js/query/date.ts | 10 ++--- assets/js/query/number.ts | 2 +- assets/js/query/user.ts | 2 +- assets/js/quick-tag.js | 14 +++---- assets/js/search.ts | 2 +- assets/js/settings.ts | 2 +- assets/js/slider.ts | 2 +- assets/js/sources.ts | 2 +- assets/js/staffhider.ts | 2 +- assets/js/tags.ts | 2 +- assets/js/tagsinput.js | 6 +-- assets/js/tagsmisc.ts | 4 +- assets/js/ujs.ts | 6 +-- assets/js/upload.js | 10 ++--- assets/js/utils/__tests__/draggable.spec.ts | 2 +- assets/js/utils/__tests__/events.spec.ts | 4 +- assets/js/utils/__tests__/image.spec.ts | 6 +-- assets/js/utils/dom.ts | 18 ++++----- assets/js/utils/draggable.ts | 2 +- assets/js/utils/events.ts | 2 +- assets/js/utils/image.ts | 2 +- assets/js/utils/local-autocompleter.ts | 2 +- assets/js/utils/tag.ts | 10 ++--- assets/test/mock-storage.ts | 4 +- assets/vite.config.ts | 4 +- 45 files changed, 154 insertions(+), 154 deletions(-) diff --git a/assets/.prettierrc.yml b/assets/.prettierrc.yml index 05ab046f5..83cfc9713 100644 --- a/assets/.prettierrc.yml +++ b/assets/.prettierrc.yml @@ -7,7 +7,7 @@ bracketSpacing: true endOfLine: lf quoteProps: as-needed trailingComma: all -arrowParens: always +arrowParens: avoid overrides: - files: "*.css" options: diff --git a/assets/js/__tests__/ujs.spec.ts b/assets/js/__tests__/ujs.spec.ts index 282eb4040..6833679e1 100644 --- a/assets/js/__tests__/ujs.spec.ts +++ b/assets/js/__tests__/ujs.spec.ts @@ -70,10 +70,10 @@ describe('Remote utilities', () => { }); it('should emit fetchcomplete event', () => - new Promise((resolve) => { + new Promise(resolve => { let a: HTMLAnchorElement | null = null; - addOneShotEventListener('fetchcomplete', (event) => { + addOneShotEventListener('fetchcomplete', event => { expect(event.target).toBe(a); resolve(); }); @@ -95,8 +95,8 @@ describe('Remote utilities', () => { }; it('should submit a form with the given action', () => - new Promise((resolve) => { - addOneShotEventListener('submit', (event) => { + new Promise(resolve => { + addOneShotEventListener('submit', event => { event.preventDefault(); const target = assertType(event.target, HTMLFormElement); @@ -192,10 +192,10 @@ describe('Remote utilities', () => { }); it('should emit fetchcomplete event', () => - new Promise((resolve) => { + new Promise(resolve => { let form: HTMLFormElement | null = null; - addOneShotEventListener('fetchcomplete', (event) => { + addOneShotEventListener('fetchcomplete', event => { expect(event.target).toBe(form); resolve(); }); @@ -214,7 +214,7 @@ describe('Remote utilities', () => { describe('Form utilities', () => { beforeEach(() => { - vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { + vi.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => { cb(1); return 1; }); @@ -260,7 +260,7 @@ describe('Form utilities', () => { // jsdom has no implementation for HTMLFormElement.prototype.submit // and will return an error if the event's default isn't prevented - form.addEventListener('submit', (event) => event.preventDefault()); + form.addEventListener('submit', event => event.preventDefault()); const button = document.createElement('button'); button.type = 'submit'; diff --git a/assets/js/__tests__/upload.spec.ts b/assets/js/__tests__/upload.spec.ts index 1003b5223..9a1d7c4c4 100644 --- a/assets/js/__tests__/upload.spec.ts +++ b/assets/js/__tests__/upload.spec.ts @@ -137,8 +137,8 @@ describe('Image upload form', () => { const failedUnloadEvent = new Event('beforeunload', { cancelable: true }); expect(fireEvent(window, failedUnloadEvent)).toBe(false); - await new Promise((resolve) => { - form.addEventListener('submit', (event) => { + await new Promise(resolve => { + form.addEventListener('submit', event => { event.preventDefault(); resolve(); }); @@ -153,7 +153,7 @@ describe('Image upload form', () => { fetchMock.mockResolvedValue(new Response(JSON.stringify(scrapeResponse), { status: 200 })); fireEvent.input(remoteUrl, { target: { value: 'http://localhost/images/1' } }); - await new Promise((resolve) => { + await new Promise(resolve => { tagsEl.addEventListener('addtag', (event: Event) => { expect((event as CustomEvent).detail).toEqual({ name: 'artist:test' }); resolve(); diff --git a/assets/js/autocomplete.js b/assets/js/autocomplete.js index 01ea28936..1a95fb046 100644 --- a/assets/js/autocomplete.js +++ b/assets/js/autocomplete.js @@ -139,7 +139,7 @@ function createList(suggestions) { list = document.createElement('ul'); list.className = 'autocomplete__list'; - suggestions.forEach((suggestion) => createItem(list, suggestion)); + suggestions.forEach(suggestion => createItem(list, suggestion)); parent.appendChild(list); } @@ -176,7 +176,7 @@ function showAutocomplete(suggestions, fetchedTerm, targetInput) { function getSuggestions(term) { // In case source URL was not given at all, do not try sending the request. if (!inputField.dataset.acSource) return []; - return fetch(`${inputField.dataset.acSource}${term}`).then((response) => response.json()); + return fetch(`${inputField.dataset.acSource}${term}`).then(response => response.json()); } function getSelectedTerm() { @@ -212,7 +212,7 @@ function listenAutocomplete() { document.addEventListener('focusin', fetchLocalAutocomplete); - document.addEventListener('input', (event) => { + document.addEventListener('input', event => { removeParent(); fetchLocalAutocomplete(event); window.clearTimeout(timeout); @@ -258,7 +258,7 @@ function listenAutocomplete() { showAutocomplete(cache[fetchedTerm], fetchedTerm, event.target); } else { // inputField could get overwritten while the suggestions are being fetched - use event.target - getSuggestions(fetchedTerm).then((suggestions) => { + getSuggestions(fetchedTerm).then(suggestions => { if (fetchedTerm === event.target.value) { showAutocomplete(suggestions, fetchedTerm, event.target); } @@ -269,7 +269,7 @@ function listenAutocomplete() { }); // If there's a click outside the inputField, remove autocomplete - document.addEventListener('click', (event) => { + document.addEventListener('click', event => { if (event.target && event.target !== inputField) removeParent(); if (event.target === inputField && isSearchField() && isSelectionOutsideCurrentTerm()) removeParent(); }); @@ -283,8 +283,8 @@ function listenAutocomplete() { fetch(`/autocomplete/compiled?vsn=2&key=${cacheKey}`, { credentials: 'omit', cache: 'force-cache' }) .then(handleError) - .then((resp) => resp.arrayBuffer()) - .then((buf) => { + .then(resp => resp.arrayBuffer()) + .then(buf => { localAc = new LocalAutocompleter(buf); }); } diff --git a/assets/js/booru.js b/assets/js/booru.js index 45f306806..3c58a7cfd 100644 --- a/assets/js/booru.js +++ b/assets/js/booru.js @@ -27,7 +27,7 @@ function isStale(tag) { } function clearTags() { - Object.keys(localStorage).forEach((key) => { + Object.keys(localStorage).forEach(key => { if (key.substring(0, 9) === 'bor_tags_') { store.remove(key); } @@ -84,8 +84,8 @@ function fetchAndPersistTags(tagIds) { const remaining = tagIds.slice(41); fetch(`/fetch/tags?ids[]=${ids.join('&ids[]=')}`) - .then((response) => response.json()) - .then((data) => data.tags.forEach((tag) => persistTag(tag))) + .then(response => response.json()) + .then(data => data.tags.forEach(tag => persistTag(tag))) .then(() => fetchAndPersistTags(remaining)); } @@ -96,7 +96,7 @@ function fetchAndPersistTags(tagIds) { function fetchNewOrStaleTags(tagIds) { const fetchIds = []; - tagIds.forEach((t) => { + tagIds.forEach(t => { const stored = store.get(`bor_tags_${t}`); if (!stored || isStale(stored)) { fetchIds.push(t); diff --git a/assets/js/boorujs.js b/assets/js/boorujs.js index 6ccfa0e4b..7f127f807 100644 --- a/assets/js/boorujs.js +++ b/assets/js/boorujs.js @@ -26,27 +26,27 @@ const types = { const actions = { hide(data) { - selectorCb(data.base, data.value, (el) => el.classList.add('hidden')); + selectorCb(data.base, data.value, el => el.classList.add('hidden')); }, tabHide(data) { - selectorCbChildren(data.base, data.value, (el) => el.classList.add('hidden')); + selectorCbChildren(data.base, data.value, el => el.classList.add('hidden')); }, show(data) { - selectorCb(data.base, data.value, (el) => el.classList.remove('hidden')); + selectorCb(data.base, data.value, el => el.classList.remove('hidden')); }, toggle(data) { - selectorCb(data.base, data.value, (el) => el.classList.toggle('hidden')); + selectorCb(data.base, data.value, el => el.classList.toggle('hidden')); }, submit(data) { - selectorCb(data.base, data.value, (el) => el.submit()); + selectorCb(data.base, data.value, el => el.submit()); }, disable(data) { - selectorCb(data.base, data.value, (el) => { + selectorCb(data.base, data.value, el => { el.disabled = true; }); }, @@ -65,7 +65,7 @@ const actions = { }, checkall(data) { - $$(`${data.value} input[type=checkbox]`).forEach((c) => { + $$(`${data.value} input[type=checkbox]`).forEach(c => { c.checked = !c.checked; }); }, @@ -102,8 +102,8 @@ const actions = { if (loadTab && !newTab.dataset.loaded) { fetchHtml(loadTab) .then(handleError) - .then((response) => response.text()) - .then((response) => { + .then(response => response.text()) + .then(response => { newTab.innerHTML = response; }) .then(() => { diff --git a/assets/js/burger.ts b/assets/js/burger.ts index a22b06ad3..58c416bbb 100644 --- a/assets/js/burger.ts +++ b/assets/js/burger.ts @@ -40,7 +40,7 @@ function copyUserLinksTo(burger: HTMLElement) { } }; - $$('.js-burger-links').forEach((container) => copy(container.children)); + $$('.js-burger-links').forEach(container => copy(container.children)); } export function setupBurgerMenu() { @@ -53,7 +53,7 @@ export function setupBurgerMenu() { copyUserLinksTo(burger); - toggle.addEventListener('click', (event) => { + toggle.addEventListener('click', event => { event.stopPropagation(); event.preventDefault(); diff --git a/assets/js/comment.js b/assets/js/comment.js index b60a6fecb..b28bcd313 100644 --- a/assets/js/comment.js +++ b/assets/js/comment.js @@ -27,7 +27,7 @@ function commentPosted(response) { commentEditForm.reset(); if (requestOk) { - response.text().then((text) => { + response.text().then(text => { if (text.includes('
')) { window.location.reload(); } else { @@ -60,7 +60,7 @@ function loadParentPost(event) { fetchHtml(`/images/${imageId}/comments/${commentId}`) .then(handleError) - .then((data) => { + .then(data => { clearParentPost(clickedLink, fullComment); insertParentPost(data, clickedLink, fullComment); }); @@ -97,7 +97,7 @@ function clearParentPost(clickedLink, fullComment) { } // Remove class active_reply_link from all links in the comment - [].slice.call(fullComment.getElementsByClassName('active_reply_link')).forEach((link) => { + [].slice.call(fullComment.getElementsByClassName('active_reply_link')).forEach(link => { link.classList.remove('active_reply_link'); }); @@ -129,7 +129,7 @@ function loadComments(event) { fetchHtml(getURL) .then(handleError) - .then((data) => { + .then(data => { displayComments(container, data); // Make sure the :target CSS selector applies to the inserted content @@ -165,7 +165,7 @@ function setupComments() { '#js-refresh-comments': loadComments, }; - document.addEventListener('click', (event) => { + document.addEventListener('click', event => { if (event.button === 0) { // Left-click only for (const target in targets) { @@ -176,7 +176,7 @@ function setupComments() { } }); - document.addEventListener('fetchcomplete', (event) => { + document.addEventListener('fetchcomplete', event => { if (event.target.id === 'js-comment-form') commentPosted(event.detail); }); } diff --git a/assets/js/fp.ts b/assets/js/fp.ts index 25fc7bd90..8302029d8 100644 --- a/assets/js/fp.ts +++ b/assets/js/fp.ts @@ -102,8 +102,8 @@ function getUserAgentBrands(): string { // NB: Chromium implements GREASE protocol to prevent ossification of // the "Not a brand" string - see https://stackoverflow.com/a/64443187 brands = data.brands - .filter((e) => !e.brand.match(/.*ot.*rand.*/gi)) - .map((e) => `${e.brand}${e.version}`) + .filter(e => !e.brand.match(/.*ot.*rand.*/gi)) + .map(e => `${e.brand}${e.version}`) .sort() .join(''); } diff --git a/assets/js/galleries.ts b/assets/js/galleries.ts index 73f0a0b6a..bf00c3c65 100644 --- a/assets/js/galleries.ts +++ b/assets/js/galleries.ts @@ -22,7 +22,7 @@ export function setupGalleryEditing() { initDraggables(); - $$('.media-box', containerEl).forEach((i) => { + $$('.media-box', containerEl).forEach(i => { i.draggable = true; }); @@ -35,7 +35,7 @@ export function setupGalleryEditing() { sortableEl.classList.remove('editing'); containerEl.classList.remove('drag-container'); - newImages = $$('.image-container', containerEl).map((i) => + newImages = $$('.image-container', containerEl).map(i => parseInt(assertNotUndefined(i.dataset.imageId), 10), ); diff --git a/assets/js/graph.ts b/assets/js/graph.ts index 035e33ed7..19dc00d21 100644 --- a/assets/js/graph.ts +++ b/assets/js/graph.ts @@ -22,7 +22,7 @@ function graphSlice(el: SVGSVGElement, width: number, offset: number) { } function resizeGraphs() { - $$('#js-graph-svg').forEach((el) => { + $$('#js-graph-svg').forEach(el => { const parent: HTMLElement | null = el.parentElement; if (parent) { @@ -47,7 +47,7 @@ function scaleGraph(target: HTMLElement, min: number, max: number) { } function setupSliders() { - $$('#js-graph-slider').forEach((el) => { + $$('#js-graph-slider').forEach(el => { const targetId = el.getAttribute('data-target'); if (!targetId) return; diff --git a/assets/js/image_expansion.js b/assets/js/image_expansion.js index 78d90ee31..10f1130ec 100644 --- a/assets/js/image_expansion.js +++ b/assets/js/image_expansion.js @@ -156,7 +156,7 @@ function bindImageForClick(target) { } function bindImageTarget(node = document) { - $$('.image-target', node).forEach((target) => { + $$('.image-target', node).forEach(target => { pickAndResize(target); if (target.dataset.mimeType === 'video/webm') { diff --git a/assets/js/imagesclientside.ts b/assets/js/imagesclientside.ts index 2d6d81414..8ea43a542 100644 --- a/assets/js/imagesclientside.ts +++ b/assets/js/imagesclientside.ts @@ -84,15 +84,15 @@ export function filterNode(node: Pick) { // Image thumb boxes with vote and fave buttons on them $$('.image-container', node) - .filter((img) => !run(img, hiddenTags, hiddenFilter, hideThumbTyped)) - .filter((img) => !run(img, spoileredTags, spoileredFilter, spoilerThumbTyped)) - .forEach((img) => showThumb(img)); + .filter(img => !run(img, hiddenTags, hiddenFilter, hideThumbTyped)) + .filter(img => !run(img, spoileredTags, spoileredFilter, spoilerThumbTyped)) + .forEach(img => showThumb(img)); // Individual image pages and images in posts/comments $$('.image-show-container', node) - .filter((img) => !run(img, hiddenTags, hiddenFilter, hideBlockTyped)) - .filter((img) => !run(img, spoileredTags, spoileredFilter, spoilerBlockTyped)) - .forEach((img) => showBlock(img)); + .filter(img => !run(img, hiddenTags, hiddenFilter, hideBlockTyped)) + .filter(img => !run(img, spoileredTags, spoileredFilter, spoilerBlockTyped)) + .forEach(img => showBlock(img)); } export function initImagesClientside() { diff --git a/assets/js/input-duplicator.ts b/assets/js/input-duplicator.ts index 6ce8a5028..57cbc1f4b 100644 --- a/assets/js/input-duplicator.ts +++ b/assets/js/input-duplicator.ts @@ -41,7 +41,7 @@ export function inputDuplicatorCreator({ const maxOptionCountElement = assertNotNull($(maxInputCountSelector, form)); const maxOptionCount = parseInt(maxOptionCountElement.innerHTML, 10); - addButton.addEventListener('click', (e) => { + addButton.addEventListener('click', e => { e.preventDefault(); const existingFields = $$(fieldSelector, form); @@ -52,7 +52,7 @@ export function inputDuplicatorCreator({ const prevField = existingFields[existingFieldsLength - 1]; const prevFieldCopy = prevField.cloneNode(true) as HTMLElement; - $$('input', prevFieldCopy).forEach((prevFieldCopyInput) => { + $$('input', prevFieldCopy).forEach(prevFieldCopyInput => { // Reset new input's value prevFieldCopyInput.value = ''; prevFieldCopyInput.removeAttribute('value'); diff --git a/assets/js/interactions.js b/assets/js/interactions.js index fac8b5e92..fc9994a8a 100644 --- a/assets/js/interactions.js +++ b/assets/js/interactions.js @@ -39,33 +39,33 @@ function modifyCache(callback) { } function cacheStatus(imageId, interactionType, value) { - modifyCache((cache) => { + modifyCache(cache => { cache[`${imageId}${interactionType}`] = { imageId, interactionType, value }; return cache; }); } function uncacheStatus(imageId, interactionType) { - modifyCache((cache) => { + modifyCache(cache => { delete cache[`${imageId}${interactionType}`]; return cache; }); } function setScore(imageId, data) { - onImage(imageId, '.score', (el) => { + onImage(imageId, '.score', el => { el.textContent = data.score; }); - onImage(imageId, '.favorites', (el) => { + onImage(imageId, '.favorites', el => { el.textContent = data.faves; }); - onImage(imageId, '.upvotes', (el) => { + onImage(imageId, '.upvotes', el => { el.textContent = data.upvotes; }); - onImage(imageId, '.downvotes', (el) => { + onImage(imageId, '.downvotes', el => { el.textContent = data.downvotes; }); } @@ -75,50 +75,50 @@ function setScore(imageId, data) { function showUpvoted(imageId) { cacheStatus(imageId, 'voted', 'up'); - onImage(imageId, '.interaction--upvote', (el) => el.classList.add('active')); + onImage(imageId, '.interaction--upvote', el => el.classList.add('active')); } function showDownvoted(imageId) { cacheStatus(imageId, 'voted', 'down'); - onImage(imageId, '.interaction--downvote', (el) => el.classList.add('active')); + onImage(imageId, '.interaction--downvote', el => el.classList.add('active')); } function showFaved(imageId) { cacheStatus(imageId, 'faved', ''); - onImage(imageId, '.interaction--fave', (el) => el.classList.add('active')); + onImage(imageId, '.interaction--fave', el => el.classList.add('active')); } function showHidden(imageId) { cacheStatus(imageId, 'hidden', ''); - onImage(imageId, '.interaction--hide', (el) => el.classList.add('active')); + onImage(imageId, '.interaction--hide', el => el.classList.add('active')); } function resetVoted(imageId) { uncacheStatus(imageId, 'voted'); - onImage(imageId, '.interaction--upvote', (el) => el.classList.remove('active')); + onImage(imageId, '.interaction--upvote', el => el.classList.remove('active')); - onImage(imageId, '.interaction--downvote', (el) => el.classList.remove('active')); + onImage(imageId, '.interaction--downvote', el => el.classList.remove('active')); } function resetFaved(imageId) { uncacheStatus(imageId, 'faved'); - onImage(imageId, '.interaction--fave', (el) => el.classList.remove('active')); + onImage(imageId, '.interaction--fave', el => el.classList.remove('active')); } function resetHidden(imageId) { uncacheStatus(imageId, 'hidden'); - onImage(imageId, '.interaction--hide', (el) => el.classList.remove('active')); + onImage(imageId, '.interaction--hide', el => el.classList.remove('active')); } function interact(type, imageId, method, data = {}) { return fetchJson(method, endpoints[type](imageId), data) - .then((res) => res.json()) - .then((res) => setScore(imageId, res)); + .then(res => res.json()) + .then(res => setScore(imageId, res)); } function displayInteractionSet(interactions) { - interactions.forEach((i) => { + interactions.forEach(i => { switch (i.interaction_type) { case 'faved': showFaved(i.image_id); @@ -143,8 +143,8 @@ function loadInteractions() { if (!document.getElementById('imagelist-container')) return; /* Users will blind downvote without this */ - window.booru.imagesWithDownvotingDisabled.forEach((i) => { - onImage(i, '.interaction--downvote', (a) => { + window.booru.imagesWithDownvotingDisabled.forEach(i => { + onImage(i, '.interaction--downvote', a => { // TODO Use a 'js-' class to target these instead const icon = a.querySelector('i') || a.querySelector('.oc-icon-small'); @@ -152,7 +152,7 @@ function loadInteractions() { a.classList.add('disabled'); a.addEventListener( 'click', - (event) => { + event => { event.stopPropagation(); event.preventDefault(); }, @@ -205,7 +205,7 @@ const targets = { }; function bindInteractions() { - document.addEventListener('click', (event) => { + document.addEventListener('click', event => { if (event.button === 0) { // Is it a left-click? for (const target in targets) { @@ -222,7 +222,7 @@ function bindInteractions() { } function loggedOutInteractions() { - [].forEach.call(document.querySelectorAll('.interaction--fave,.interaction--upvote,.interaction--downvote'), (a) => + [].forEach.call(document.querySelectorAll('.interaction--fave,.interaction--upvote,.interaction--downvote'), a => a.setAttribute('href', '/sessions/new'), ); } diff --git a/assets/js/markdowntoolbar.js b/assets/js/markdowntoolbar.js index 93ce1af9a..47a0e104d 100644 --- a/assets/js/markdowntoolbar.js +++ b/assets/js/markdowntoolbar.js @@ -156,13 +156,13 @@ function insertLink(textarea, options) { } function wrapSelection(textarea, options) { - transformSelection(textarea, (selectedText) => { + transformSelection(textarea, selectedText => { const { text = selectedText, prefix = '', suffix = options.prefix } = options, emptyText = text === ''; let newText = text; if (!emptyText) { - newText = text.replace(/(\n{2,})/g, (match) => { + newText = text.replace(/(\n{2,})/g, match => { return suffix + match + prefix; }); } @@ -188,7 +188,7 @@ function wrapLines(textarea, options, eachLine = true) { ? prefix + text.trim() + suffix : text .split(/\n/g) - .map((line) => prefix + line.trim() + suffix) + .map(line => prefix + line.trim() + suffix) .join('\n'); // Force a space at the end of lines with only blockquote markers @@ -205,7 +205,7 @@ function wrapSelectionOrLines(textarea, options) { } function escapeSelection(textarea, options) { - transformSelection(textarea, (selectedText) => { + transformSelection(textarea, selectedText => { const { text = selectedText } = options, emptyText = text === ''; @@ -255,10 +255,10 @@ function shortcutHandler(event) { } function setupToolbar() { - $$('.communication__toolbar').forEach((toolbar) => { + $$('.communication__toolbar').forEach(toolbar => { toolbar.addEventListener('click', clickHandler); }); - $$('.js-toolbar-input').forEach((textarea) => { + $$('.js-toolbar-input').forEach(textarea => { textarea.addEventListener('keydown', shortcutHandler); }); } diff --git a/assets/js/misc.ts b/assets/js/misc.ts index ef9eb7f98..65c4e0ed0 100644 --- a/assets/js/misc.ts +++ b/assets/js/misc.ts @@ -20,7 +20,7 @@ function formResult({ target, detail }: FetchcompleteEvent) { hideEl(formEl); showEl(resultEl); - $$('input[type="submit"],button', formEl).forEach((button) => { + $$('input[type="submit"],button', formEl).forEach(button => { button.disabled = false; }); } @@ -30,7 +30,7 @@ function formResult({ target, detail }: FetchcompleteEvent) { const form = assertType(target, HTMLFormElement); const result = assertNotNull($(resultSelector)); - detail.text().then((text) => showResult(form, result, text)); + detail.text().then(text => showResult(form, result, text)); } } } @@ -85,7 +85,7 @@ export function setupEvents() { } if (store.get('hide_score')) { - $$('.upvotes,.score,.downvotes').forEach((s) => hideEl(s)); + $$('.upvotes,.score,.downvotes').forEach(s => hideEl(s)); } document.addEventListener('fetchcomplete', formResult); diff --git a/assets/js/notifications.ts b/assets/js/notifications.ts index 4d4757b9a..7d007b264 100644 --- a/assets/js/notifications.ts +++ b/assets/js/notifications.ts @@ -13,10 +13,10 @@ const NOTIFICATION_INTERVAL = 600000, function bindSubscriptionLinks() { delegate(document, 'fetchcomplete', { - '.js-subscription-link': (event) => { + '.js-subscription-link': event => { const target = assertNotNull(event.target.closest('.js-subscription-target')); - event.detail.text().then((text) => { + event.detail.text().then(text => { target.outerHTML = text; }); }, @@ -30,7 +30,7 @@ function getNewNotifications() { fetchJson('GET', '/notifications/unread') .then(handleError) - .then((response) => response.json()) + .then(response => response.json()) .then(({ notifications }) => { updateNotificationTicker(notifications); storeNotificationCount(notifications); diff --git a/assets/js/preview.js b/assets/js/preview.js index 6988b3c0a..21dc43a4c 100644 --- a/assets/js/preview.js +++ b/assets/js/preview.js @@ -47,7 +47,7 @@ function getPreview(body, anonymous, previewLoading, previewIdle, previewContent fetchJson('POST', path, { body, anonymous }) .then(handleError) - .then((data) => { + .then(data => { previewContent.innerHTML = data; filterNode(previewContent); bindImageTarget(previewContent); @@ -117,7 +117,7 @@ function setupPreviews() { updatePreview(); }); - document.addEventListener('click', (event) => { + document.addEventListener('click', event => { if (event.target && event.target.closest('.post-reply')) { const link = event.target.closest('.post-reply'); commentReply(link.dataset.author, link.getAttribute('href'), textarea, link.dataset.post); diff --git a/assets/js/query/boolean.ts b/assets/js/query/boolean.ts index 6f2afe964..68a0b1073 100644 --- a/assets/js/query/boolean.ts +++ b/assets/js/query/boolean.ts @@ -1,11 +1,11 @@ import { AstMatcher } from './types'; export function matchAny(...matchers: AstMatcher[]): AstMatcher { - return (e: HTMLElement) => matchers.some((matcher) => matcher(e)); + return (e: HTMLElement) => matchers.some(matcher => matcher(e)); } export function matchAll(...matchers: AstMatcher[]): AstMatcher { - return (e: HTMLElement) => matchers.every((matcher) => matcher(e)); + return (e: HTMLElement) => matchers.every(matcher => matcher(e)); } export function matchNot(matcher: AstMatcher): AstMatcher { diff --git a/assets/js/query/date.ts b/assets/js/query/date.ts index 17b1e5897..a3404afdb 100644 --- a/assets/js/query/date.ts +++ b/assets/js/query/date.ts @@ -17,16 +17,16 @@ function makeMatcher(bottomDate: PosixTimeMs, topDate: PosixTimeMs, qual: RangeE // done compared to numeric ranges. switch (qual) { case 'lte': - return (v) => new Date(v).getTime() < topDate; + return v => new Date(v).getTime() < topDate; case 'gte': - return (v) => new Date(v).getTime() >= bottomDate; + return v => new Date(v).getTime() >= bottomDate; case 'lt': - return (v) => new Date(v).getTime() < bottomDate; + return v => new Date(v).getTime() < bottomDate; case 'gt': - return (v) => new Date(v).getTime() >= topDate; + return v => new Date(v).getTime() >= topDate; case 'eq': default: - return (v) => { + return v => { const t = new Date(v).getTime(); return t >= bottomDate && t < topDate; }; diff --git a/assets/js/query/number.ts b/assets/js/query/number.ts index 5fe08e727..8c42db300 100644 --- a/assets/js/query/number.ts +++ b/assets/js/query/number.ts @@ -2,7 +2,7 @@ import { FieldMatcher, RangeEqualQualifier } from './types'; export function makeNumberMatcher(term: number, fuzz: number, qual: RangeEqualQualifier): FieldMatcher { // Range matching. - return (v) => { + return v => { const attrVal = parseFloat(v); if (isNaN(attrVal)) { diff --git a/assets/js/query/user.ts b/assets/js/query/user.ts index 2b8dbfe8d..1410813fe 100644 --- a/assets/js/query/user.ts +++ b/assets/js/query/user.ts @@ -8,7 +8,7 @@ function interactionMatch( interactions: Interaction[], ): boolean { return interactions.some( - (v) => v.image_id === imageId && v.interaction_type === type && (value === null || v.value === value), + v => v.image_id === imageId && v.interaction_type === type && (value === null || v.value === value), ); } diff --git a/assets/js/quick-tag.js b/assets/js/quick-tag.js index e43b460e7..c75a37d5f 100644 --- a/assets/js/quick-tag.js +++ b/assets/js/quick-tag.js @@ -30,10 +30,10 @@ function toggleActiveState() { setTagButton(`Submit (${currentTags()})`); - $$('.media-box__header').forEach((el) => el.classList.toggle('media-box__header--unselected')); - $$('.media-box__header').forEach((el) => el.classList.remove('media-box__header--selected')); - currentQueue().forEach((id) => - $$(`.media-box__header[data-image-id="${id}"]`).forEach((el) => el.classList.add('media-box__header--selected')), + $$('.media-box__header').forEach(el => el.classList.toggle('media-box__header--unselected')); + $$('.media-box__header').forEach(el => el.classList.remove('media-box__header--selected')); + currentQueue().forEach(id => + $$(`.media-box__header[data-image-id="${id}"]`).forEach(el => el.classList.add('media-box__header--selected')), ); } @@ -64,8 +64,8 @@ function submit() { image_ids: currentQueue(), }) .then(handleError) - .then((r) => r.json()) - .then((data) => { + .then(r => r.json()) + .then(data => { if (data.failed.length) window.alert(`Failed to add tags to the images with these IDs: ${data.failed}`); reset(); @@ -80,7 +80,7 @@ function modifyImageQueue(mediaBox) { isSelected ? queue.splice(queue.indexOf(imageId), 1) : queue.push(imageId); - $$(`.media-box__header[data-image-id="${imageId}"]`).forEach((el) => + $$(`.media-box__header[data-image-id="${imageId}"]`).forEach(el => el.classList.toggle('media-box__header--selected'), ); diff --git a/assets/js/search.ts b/assets/js/search.ts index 351105ea8..269497d26 100644 --- a/assets/js/search.ts +++ b/assets/js/search.ts @@ -2,7 +2,7 @@ import { $, $$ } from './utils/dom'; import { addTag } from './tagsinput'; function showHelp(subject: string, type: string | null) { - $$('[data-search-help]').forEach((helpBox) => { + $$('[data-search-help]').forEach(helpBox => { if (helpBox.getAttribute('data-search-help') === type) { const searchSubject = $('.js-search-help-subject', helpBox); diff --git a/assets/js/settings.ts b/assets/js/settings.ts index ed4256b4d..f40fa4392 100644 --- a/assets/js/settings.ts +++ b/assets/js/settings.ts @@ -18,7 +18,7 @@ export function setupSettings() { const styleSheet = assertNotNull($('#js-theme-stylesheet')); // Local settings - localCheckboxes.forEach((checkbox) => { + localCheckboxes.forEach(checkbox => { checkbox.addEventListener('change', () => { store.set(checkbox.id.replace('user_', ''), checkbox.checked); }); diff --git a/assets/js/slider.ts b/assets/js/slider.ts index 95ca83fc6..73edc488b 100644 --- a/assets/js/slider.ts +++ b/assets/js/slider.ts @@ -169,7 +169,7 @@ function setupSlider(el: HTMLInputElement) { // Sets up all sliders currently on the page. function setupSliders() { - $$('input[type="dualrange"]').forEach((el) => { + $$('input[type="dualrange"]').forEach(el => { setupSlider(el); }); } diff --git a/assets/js/sources.ts b/assets/js/sources.ts index 0a0aab18c..b4bc51ecf 100644 --- a/assets/js/sources.ts +++ b/assets/js/sources.ts @@ -19,7 +19,7 @@ export function imageSourcesCreator() { if (target.matches('#source-form')) { const sourceSauce = assertNotNull($('.js-sourcesauce')); - detail.text().then((text) => { + detail.text().then(text => { sourceSauce.outerHTML = text; setupInputs(); }); diff --git a/assets/js/staffhider.ts b/assets/js/staffhider.ts index 23aa38990..86741d785 100644 --- a/assets/js/staffhider.ts +++ b/assets/js/staffhider.ts @@ -8,6 +8,6 @@ import { $$, hideEl } from './utils/dom'; export function hideStaffTools() { if (window.booru.hideStaffTools === 'true') { - $$('.js-staff-action').forEach((el) => hideEl(el)); + $$('.js-staff-action').forEach(el => hideEl(el)); } } diff --git a/assets/js/tags.ts b/assets/js/tags.ts index 4e7f9191d..deb3c7bd4 100644 --- a/assets/js/tags.ts +++ b/assets/js/tags.ts @@ -82,7 +82,7 @@ function createTagDropdown(tag: HTMLSpanElement) { if (!userIsSignedIn) showEl(signIn); if (userIsSignedIn && !userCanEditFilter) showEl(filter); - tag.addEventListener('fetchcomplete', (event) => { + tag.addEventListener('fetchcomplete', event => { const act = assertNotUndefined(event.target.dataset.tagAction); actions[act](); }); diff --git a/assets/js/tagsinput.js b/assets/js/tagsinput.js index 10e9fd61d..bc05b3d7d 100644 --- a/assets/js/tagsinput.js +++ b/assets/js/tagsinput.js @@ -81,7 +81,7 @@ function setupTagsInput(tagBlock) { // enter or comma if (keyCode === 13 || (keyCode === 188 && !shiftKey)) { event.preventDefault(); - inputField.value.split(',').forEach((t) => insertTag(t)); + inputField.value.split(',').forEach(t => insertTag(t)); inputField.value = ''; } } @@ -129,7 +129,7 @@ function setupTagsInput(tagBlock) { container.appendChild(inputField); tags = []; - textarea.value.split(',').forEach((t) => insertTag(t)); + textarea.value.split(',').forEach(t => insertTag(t)); textarea.value = tags.join(', '); } } @@ -143,7 +143,7 @@ function fancyEditorRequested(tagBlock) { } function setupTagListener() { - document.addEventListener('addtag', (event) => { + document.addEventListener('addtag', event => { if (event.target.value) event.target.value += ', '; event.target.value += event.detail.name; }); diff --git a/assets/js/tagsmisc.ts b/assets/js/tagsmisc.ts index acf657029..17fdf6d34 100644 --- a/assets/js/tagsmisc.ts +++ b/assets/js/tagsmisc.ts @@ -38,7 +38,7 @@ function tagInputButtons(event: MouseEvent) { } function setupTags() { - $$('.js-tag-block').forEach((el) => { + $$('.js-tag-block').forEach(el => { setupTagsInput(el); el.classList.remove('js-tag-block'); }); @@ -48,7 +48,7 @@ function updateTagSauce({ target, detail }: FetchcompleteEvent) { if (target.matches('#tags-form')) { const tagSauce = assertNotNull($('.js-tagsauce')); - detail.text().then((text) => { + detail.text().then(text => { tagSauce.outerHTML = text; setupTags(); initTagDropdown(); diff --git a/assets/js/ujs.ts b/assets/js/ujs.ts index 626f8c991..b2905806f 100644 --- a/assets/js/ujs.ts +++ b/assets/js/ujs.ts @@ -57,7 +57,7 @@ function formRemote(event: Event, target: HTMLFormElement) { method: (target.dataset.method || target.method).toUpperCase(), headers: headers(), body: new FormData(target), - }).then((response) => { + }).then(response => { fire(target, 'fetchcomplete', response); if (response && response.status === 300) { window.location.reload(); @@ -66,7 +66,7 @@ function formRemote(event: Event, target: HTMLFormElement) { } function formReset(_event: Event | null, target: HTMLElement) { - $$('[disabled][data-disable-with][data-enable-with]', target).forEach((input) => { + $$('[disabled][data-disable-with][data-enable-with]', target).forEach(input => { const label = findFirstTextNode(input); if (label) { label.nodeValue = ` ${input.dataset.enableWith}`; @@ -85,7 +85,7 @@ function linkRemote(event: Event, target: HTMLAnchorElement) { credentials: 'same-origin', method: (target.dataset.method || 'get').toUpperCase(), headers: headers(), - }).then((response) => fire(target, 'fetchcomplete', response)); + }).then(response => fire(target, 'fetchcomplete', response)); } delegate(document, 'click', { diff --git a/assets/js/upload.js b/assets/js/upload.js index 6d0e52b53..16d339599 100644 --- a/assets/js/upload.js +++ b/assets/js/upload.js @@ -11,7 +11,7 @@ const MATROSKA_MAGIC = 0x1a45dfa3; function scrapeUrl(url) { return fetchJson('POST', '/images/scrape', { url }) .then(handleError) - .then((response) => response.json()); + .then(response => response.json()); } function elementForEmbeddedImage({ camo_url, type }) { @@ -34,7 +34,7 @@ function setupImageUpload() { const [fileField, remoteUrl, scraperError] = $$('.js-scraper', form); const descrEl = $('.js-image-descr-input', form); const tagsEl = $('.js-image-tags-input', form); - const sourceEl = $$('.js-source-url', form).find((input) => input.value === ''); + const sourceEl = $$('.js-source-url', form).find(input => input.value === ''); const fetchButton = $('#js-scraper-preview'); if (!fetchButton) return; @@ -80,7 +80,7 @@ function setupImageUpload() { const reader = new FileReader(); - reader.addEventListener('load', (event) => { + reader.addEventListener('load', event => { showImages([ { camo_url: event.target.result, @@ -107,7 +107,7 @@ function setupImageUpload() { disableFetch(); scrapeUrl(remoteUrl.value) - .then((data) => { + .then(data => { if (data === null) { scraperError.innerText = 'No image found at that address.'; showError(); @@ -136,7 +136,7 @@ function setupImageUpload() { }); // Fetch on "enter" in url field - remoteUrl.addEventListener('keydown', (event) => { + remoteUrl.addEventListener('keydown', event => { if (event.keyCode === 13) { // Hit enter fetchButton.click(); diff --git a/assets/js/utils/__tests__/draggable.spec.ts b/assets/js/utils/__tests__/draggable.spec.ts index d5415c230..7f9357a0d 100644 --- a/assets/js/utils/__tests__/draggable.spec.ts +++ b/assets/js/utils/__tests__/draggable.spec.ts @@ -80,7 +80,7 @@ describe('Draggable Utilities', () => { expect(dataTransferItem.type).toEqual('text/plain'); let stringValue: string | undefined; - dataTransferItem.getAsString((value) => { + dataTransferItem.getAsString(value => { stringValue = value; }); expect(stringValue).toEqual(''); diff --git a/assets/js/utils/__tests__/events.spec.ts b/assets/js/utils/__tests__/events.spec.ts index 8c068e5bb..575883b7a 100644 --- a/assets/js/utils/__tests__/events.spec.ts +++ b/assets/js/utils/__tests__/events.spec.ts @@ -60,7 +60,7 @@ describe('Event utils', () => { const mockButton = document.createElement('button'); const mockHandler = vi.fn(); - mockButton.addEventListener('click', (e) => leftClick(mockHandler)(e, mockButton)); + mockButton.addEventListener('click', e => leftClick(mockHandler)(e, mockButton)); fireEvent.click(mockButton, { button: 0 }); @@ -72,7 +72,7 @@ describe('Event utils', () => { const mockHandler = vi.fn(); const mockButtonNumber = getRandomArrayItem([1, 2, 3, 4, 5]); - mockButton.addEventListener('click', (e) => leftClick(mockHandler)(e, mockButton)); + mockButton.addEventListener('click', e => leftClick(mockHandler)(e, mockButton)); fireEvent.click(mockButton, { button: mockButtonNumber }); diff --git a/assets/js/utils/__tests__/image.spec.ts b/assets/js/utils/__tests__/image.spec.ts index 8c3286f5f..ded1389b8 100644 --- a/assets/js/utils/__tests__/image.spec.ts +++ b/assets/js/utils/__tests__/image.spec.ts @@ -101,7 +101,7 @@ describe('Image utils', () => { const mockImage = new Image(); mockImage.src = mockImageUri; if (imgClasses) { - imgClasses.forEach((videoClass) => { + imgClasses.forEach(videoClass => { mockImage.classList.add(videoClass); }); } @@ -109,7 +109,7 @@ describe('Image utils', () => { const mockVideo = document.createElement('video'); if (videoClasses) { - videoClasses.forEach((videoClass) => { + videoClasses.forEach(videoClass => { mockVideo.classList.add(videoClass); }); } @@ -161,7 +161,7 @@ describe('Image utils', () => { expect(result).toBe(true); }); - ['data-size', 'data-uris'].forEach((missingAttributeName) => { + ['data-size', 'data-uris'].forEach(missingAttributeName => { it(`should return early if the ${missingAttributeName} attribute is missing`, () => { const { mockElement } = createMockElements({ extension: 'webm', diff --git a/assets/js/utils/dom.ts b/assets/js/utils/dom.ts index 12114b9be..485461221 100644 --- a/assets/js/utils/dom.ts +++ b/assets/js/utils/dom.ts @@ -25,37 +25,37 @@ export function $$( } export function showEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach((el) => el.classList.remove('hidden')); + ([] as E[]).concat(...elements).forEach(el => el.classList.remove('hidden')); } export function hideEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach((el) => el.classList.add('hidden')); + ([] as E[]).concat(...elements).forEach(el => el.classList.add('hidden')); } export function toggleEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach((el) => el.classList.toggle('hidden')); + ([] as E[]).concat(...elements).forEach(el => el.classList.toggle('hidden')); } export function clearEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach((el) => { + ([] as E[]).concat(...elements).forEach(el => { while (el.firstChild) el.removeChild(el.firstChild); }); } export function disableEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach((el) => { + ([] as E[]).concat(...elements).forEach(el => { el.disabled = true; }); } export function enableEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach((el) => { + ([] as E[]).concat(...elements).forEach(el => { el.disabled = false; }); } export function removeEl(...elements: E[] | ConcatArray[]) { - ([] as E[]).concat(...elements).forEach((el) => el.parentNode?.removeChild(el)); + ([] as E[]).concat(...elements).forEach(el => el.parentNode?.removeChild(el)); } export function makeEl( @@ -78,7 +78,7 @@ export function onLeftClick( callback: (e: MouseEvent) => boolean | void, context: Pick = document, ): VoidFunction { - const handler: typeof callback = (event) => { + const handler: typeof callback = event => { if (event.button === 0) callback(event); }; context.addEventListener('click', handler); @@ -106,5 +106,5 @@ export function escapeCss(css: string): string { } export function findFirstTextNode(of: Node): N { - return Array.prototype.filter.call(of.childNodes, (el) => el.nodeType === Node.TEXT_NODE)[0]; + return Array.prototype.filter.call(of.childNodes, el => el.nodeType === Node.TEXT_NODE)[0]; } diff --git a/assets/js/utils/draggable.ts b/assets/js/utils/draggable.ts index 69c5a9d31..727066483 100644 --- a/assets/js/utils/draggable.ts +++ b/assets/js/utils/draggable.ts @@ -56,7 +56,7 @@ function dragEnd(event: DragEvent, target: HTMLElement) { clearDragSource(); if (target.parentNode) { - $$('.over', target.parentNode).forEach((t) => t.classList.remove('over')); + $$('.over', target.parentNode).forEach(t => t.classList.remove('over')); } } diff --git a/assets/js/utils/events.ts b/assets/js/utils/events.ts index 06733721e..70460bf8b 100644 --- a/assets/js/utils/events.ts +++ b/assets/js/utils/events.ts @@ -48,7 +48,7 @@ export function delegate void | boolean>, ) { - node.addEventListener(event, (e) => { + node.addEventListener(event, e => { for (const selector in selectors) { const evtTarget = e.target as EventTarget | Target | null; if (evtTarget && 'closest' in evtTarget && typeof evtTarget.closest === 'function') { diff --git a/assets/js/utils/image.ts b/assets/js/utils/image.ts index cbdbb20e6..da23f2ea6 100644 --- a/assets/js/utils/image.ts +++ b/assets/js/utils/image.ts @@ -117,7 +117,7 @@ export function spoilerThumb(img: HTMLDivElement, spoilerUri: string, reason: st switch (window.booru.spoilerType) { case 'click': - img.addEventListener('click', (event) => { + img.addEventListener('click', event => { if (showThumb(img)) event.preventDefault(); }); img.addEventListener('mouseleave', () => hideThumb(img, spoilerUri, reason)); diff --git a/assets/js/utils/local-autocompleter.ts b/assets/js/utils/local-autocompleter.ts index f27f8e2dd..ec3ba1626 100644 --- a/assets/js/utils/local-autocompleter.ts +++ b/assets/js/utils/local-autocompleter.ts @@ -133,7 +133,7 @@ export class LocalAutocompleter { } // Add if not filtering or no associations are filtered - if (unfilter || hiddenTags.findIndex((ht) => result.associations.includes(ht)) === -1) { + if (unfilter || hiddenTags.findIndex(ht => result.associations.includes(ht)) === -1) { results[result.name] = result; } } diff --git a/assets/js/utils/tag.ts b/assets/js/utils/tag.ts index 7132efcb6..fcd18d18c 100644 --- a/assets/js/utils/tag.ts +++ b/assets/js/utils/tag.ts @@ -30,7 +30,7 @@ function sortTags(hidden: boolean, a: TagData, b: TagData): number { export function getHiddenTags(): TagData[] { return unique(window.booru.hiddenTagList) - .map((tagId) => getTag(tagId)) + .map(tagId => getTag(tagId)) .sort(sortTags.bind(null, true)); } @@ -38,8 +38,8 @@ export function getSpoileredTags(): TagData[] { if (window.booru.spoilerType === 'off') return []; return unique(window.booru.spoileredTagList) - .filter((tagId) => window.booru.ignoredTagList.indexOf(tagId) === -1) - .map((tagId) => getTag(tagId)) + .filter(tagId => window.booru.ignoredTagList.indexOf(tagId) === -1) + .map(tagId => getTag(tagId)) .sort(sortTags.bind(null, false)); } @@ -49,7 +49,7 @@ export function imageHitsTags(img: HTMLElement, matchTags: TagData[]): TagData[] return []; } const imageTags = JSON.parse(imageTagsString); - return matchTags.filter((t) => imageTags.indexOf(t.id) !== -1); + return matchTags.filter(t => imageTags.indexOf(t.id) !== -1); } export function imageHitsComplex(img: HTMLElement, matchComplex: AstMatcher) { @@ -63,7 +63,7 @@ export function displayTags(tags: TagData[]): string { extras; if (otherTags.length > 0) { - extras = otherTags.map((tag) => escapeHtml(tag.name)).join(', '); + extras = otherTags.map(tag => escapeHtml(tag.name)).join(', '); list += `, ${extras}`; } diff --git a/assets/test/mock-storage.ts b/assets/test/mock-storage.ts index c05bcda6d..1f283f298 100644 --- a/assets/test/mock-storage.ts +++ b/assets/test/mock-storage.ts @@ -68,7 +68,7 @@ export function mockStorageImpl(): MockStorageImplApi { delete tempStorage[key]; }, }); - const forceStorageError: MockStorageImplApi['forceStorageError'] = (func) => { + const forceStorageError: MockStorageImplApi['forceStorageError'] = func => { shouldThrow = true; const value = func(); if (!(value instanceof Promise)) { @@ -80,7 +80,7 @@ export function mockStorageImpl(): MockStorageImplApi { shouldThrow = false; }); }; - const setStorageValue: MockStorageImplApi['setStorageValue'] = (value) => { + const setStorageValue: MockStorageImplApi['setStorageValue'] = value => { tempStorage = value; }; const clearStorage = () => setStorageValue({}); diff --git a/assets/vite.config.ts b/assets/vite.config.ts index 2eb4607d1..812f7f8b0 100644 --- a/assets/vite.config.ts +++ b/assets/vite.config.ts @@ -11,13 +11,13 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => { const isDev = command !== 'build' && mode !== 'test'; const targets = new Map(); - fs.readdirSync(path.resolve(__dirname, 'css/themes/')).forEach((name) => { + fs.readdirSync(path.resolve(__dirname, 'css/themes/')).forEach(name => { const m = name.match(/([-a-z]+).css/); if (m) targets.set(`css/${m[1]}`, `./css/themes/${m[1]}.css`); }); - fs.readdirSync(path.resolve(__dirname, 'css/options/')).forEach((name) => { + fs.readdirSync(path.resolve(__dirname, 'css/options/')).forEach(name => { const m = name.match(/([-a-z]+).css/); if (m) targets.set(`css/options/${m[1]}`, `./css/options/${m[1]}.css`); From b107330e2a8ad63c333c845cabd16b02798f9177 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Mon, 30 Sep 2024 22:32:09 +0200 Subject: [PATCH 64/65] changes --- assets/js/shortcuts.ts | 34 ++++++++++------------------------ assets/package-lock.json | 3 ++- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/assets/js/shortcuts.ts b/assets/js/shortcuts.ts index dc7d920fe..ac0745d8d 100644 --- a/assets/js/shortcuts.ts +++ b/assets/js/shortcuts.ts @@ -47,31 +47,16 @@ function isOK(event: KeyboardEvent): boolean { ); } +/* eslint-disable prettier/prettier */ const keyCodes: ShortcutKeyMap = { - j() { - click('.js-prev'); - }, // J - go to previous image - i() { - click('.js-up'); - }, // I - go to index page - k() { - click('.js-next'); - }, // K - go to next image - r() { - click('.js-rand'); - }, // R - go to random image - s() { - click('.js-source-link'); - }, // S - go to image source - l() { - click('.js-tag-sauce-toggle'); - }, // L - edit tags - o() { - openFullView(); - }, // O - open original - v() { - openFullViewNewTab(); - }, // V - open original in a new tab + j() { click('.js-prev'); }, // J - go to previous image + i() { click('.js-up'); }, // I - go to index page + k() { click('.js-next'); }, // K - go to next image + r() { click('.js-rand'); }, // R - go to random image + s() { click('.js-source-link'); }, // S - go to image source + l() { click('.js-tag-sauce-toggle'); }, // L - edit tags + o() { openFullView(); }, // O - open original + v() { openFullViewNewTab(); }, // V - open original in a new tab f() { // F - favourite image click(getHover() ? `a.interaction--fave[data-image-id="${getHover()}"]` : '.block__header a.interaction--fave'); @@ -81,6 +66,7 @@ const keyCodes: ShortcutKeyMap = { click(getHover() ? `a.interaction--upvote[data-image-id="${getHover()}"]` : '.block__header a.interaction--upvote'); }, }; +/* eslint-enable prettier/prettier */ export function listenForKeys() { document.addEventListener('keydown', (event: KeyboardEvent) => { diff --git a/assets/package-lock.json b/assets/package-lock.json index a16baf7cc..cd0063c5f 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -24,7 +24,6 @@ "@vitest/coverage-v8": "^1.6.0", "chai": "^5", "eslint": "^9.4.0", - "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-vitest": "^0.5.4", "jest-environment-jsdom": "^29.7.0", @@ -2791,6 +2790,8 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, + "optional": true, + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, From bff5adfc8ecd098e4461c5e1248f1c226fa97433 Mon Sep 17 00:00:00 2001 From: "Luna D." Date: Mon, 30 Sep 2024 23:25:22 +0200 Subject: [PATCH 65/65] fix dev seeds --- priv/repo/seeds_development.exs | 2 -- 1 file changed, 2 deletions(-) diff --git a/priv/repo/seeds_development.exs b/priv/repo/seeds_development.exs index 75f6333be..0732e1124 100644 --- a/priv/repo/seeds_development.exs +++ b/priv/repo/seeds_development.exs @@ -172,8 +172,6 @@ defmodule Philomena.DevSeeds do [ fingerprint: "d015c342859dde3", ip: default_ip(), - user_agent: "Hopefully not IE", - referrer: "localhost", user_id: id, user: user ]