diff --git a/assets/css/application.css b/assets/css/application.css index cdc7a8ed..d2d5a5e8 100644 --- a/assets/css/application.css +++ b/assets/css/application.css @@ -32,7 +32,7 @@ @import "elements/form"; @import "elements/heading"; @import "elements/input"; -@import "elements/interaction"; +@import "elements/label"; @import "elements/layout"; @import "elements/list"; @import "elements/media"; @@ -48,9 +48,11 @@ @import "views/footer"; @import "views/header"; @import "views/image"; +@import "views/interaction"; @import "views/markdown"; @import "views/metabar"; @import "views/pagination"; @import "views/staff"; @import "views/statistics"; @import "views/tag"; +@import "views/user"; diff --git a/assets/css/common/measurements.css b/assets/css/common/measurements.css index 4ed4c946..84164156 100644 --- a/assets/css/common/measurements.css +++ b/assets/css/common/measurements.css @@ -51,13 +51,16 @@ $font-family-monospace: "Droid Sans Mono", monospace; --media-container-width: 225px; --media-tiny-container-width: 50px; - --media-small-container-width: 125px; + --media-small-container-width: 150px; --media-medium-container-width: 250px; --media-large-container-width: 500px; --media-full-container-width: 100%; --media-featured-width: 358px; --media-header-height: 2rem; + --badge-small-size: 1.1rem; + --badge-normal-size: 2rem; + --number-badge-size: 0.6rem; --number-badge-padding: 0.1rem; --number-badge-border: 2px; diff --git a/assets/css/elements/button.css b/assets/css/elements/button.css index 61687500..86aed756 100644 --- a/assets/css/elements/button.css +++ b/assets/css/elements/button.css @@ -8,11 +8,11 @@ .button--$(type).button--important { color: var(--text-color) !important; background: var(--$(type)-color); - box-shadow: 0 -1px var(--$(type)-dark-color) inset; } - .button--$(type):hover { - background: var(--$(type)-muted-color) !important; + .button--$(type):hover, .button--$(type):active, .button--$(type).selected { + background: var(--$(type)-dark-color) !important; + border-radius: var(--border-radius-inner); } .button__group--$(type) { @@ -28,6 +28,7 @@ @mixin animated-transition; color: var(--text-color); background: var(--$(type)-color); + border-radius: var(--border-radius-inner); } } @@ -40,17 +41,18 @@ font-size: var(--font-size); background: var(--primary-dark-color); color: var(--text-color); - border: 0; border-radius: var(--border-radius-inner); padding: 0 var(--padding-small); overflow: hidden; line-height: var(--button-height); align-items: center; + border-width: 0; } -.button:hover { +.button:hover, .button:active { @mixin animated-transition; - background: var(--primary-muted-color); + background: var(--primary-dark-color); + border-radius: var(--border-radius-inner); cursor: pointer; } @@ -70,7 +72,6 @@ .button--important { background: var(--primary-color); - box-shadow: 0 -1px var(--primary-dark-color) inset; } .button__row { @@ -85,7 +86,6 @@ .button__group, .button__group--single, .button__group--standalone { display: flex; flex-direction: row; - border: 0; border-radius: var(--border-radius-inner); margin-right: var(--padding-normal); background: var(--secondary-dark-color); @@ -126,6 +126,7 @@ .button__group--standalone a:hover { @mixin animated-transition; background: var(--secondary-muted-color); + border-radius: var(--border-radius-inner); } .button--borderless { @@ -139,6 +140,7 @@ .block__header__buttons .button:hover { background: var(--primary-muted-color); + border-radius: var(--border-radius-inner); } @mixin button-type primary; diff --git a/assets/css/elements/flex.css b/assets/css/elements/flex.css index b39b0ff0..77132562 100644 --- a/assets/css/elements/flex.css +++ b/assets/css/elements/flex.css @@ -67,6 +67,18 @@ justify-content: space-between; } +.flex--small-gap { + gap: var(--padding-small); +} + +.flex--normal-gap { + gap: var(--padding-normal); +} + +.flex--large-gap { + gap: var(--padding-large); +} + .flex--start-bunched { justify-content: flex-start; } diff --git a/assets/css/elements/form.css b/assets/css/elements/form.css index ea3d504b..a1d38496 100644 --- a/assets/css/elements/form.css +++ b/assets/css/elements/form.css @@ -17,6 +17,36 @@ form .form--two-column > .field, form .form--two-column > li { gap: var(--padding-normal); } +form .form--three-items { + display: grid; + grid: inherit; + grid-template-columns: 1 / -1; + gap: var(--padding-normal); +} + +form .form--three-items > .field { + display: grid; + grid-template-columns: auto 1fr; + grid-template-rows: 1fr auto; + gap: var(--padding-normal); +} + +form .form--three-items > .field > *:nth-child(1) { + grid-area: 1 / 1 / 2 / 2; +} + +form .form--three-items > .field > *:nth-child(2) { + grid-area: 1 / 2 / 2 / 3; +} + +form .form--three-items > .field > *:nth-child(3) { + grid-area: 2 / 1 / 3 / 3; +} + +form .form--three-items select { + width: fit-content; +} + form .with-error { display: block; } diff --git a/assets/css/elements/label.css b/assets/css/elements/label.css new file mode 100644 index 00000000..c7599e3a --- /dev/null +++ b/assets/css/elements/label.css @@ -0,0 +1,26 @@ +@define-mixin label-type $type { + .label--$(type) { + background-color: var(--$(type)-color); + } +} + +.label { + display: flex; + background: var(--primary-color); + padding: var(--padding-tiny) var(--padding-small); + border-radius: var(--border-radius-inner); + width: fit-content; + gap: var(--padding-tiny); + white-space: nowrap; +} + +.label--block { + margin: var(--padding-small) 0; +} + +@mixin label-type secondary; +@mixin label-type danger; +@mixin label-type warning; +@mixin label-type success; +@mixin label-type special; +@mixin label-type information; diff --git a/assets/css/options/compatibility.css b/assets/css/options/compatibility.css new file mode 100644 index 00000000..35393bf6 --- /dev/null +++ b/assets/css/options/compatibility.css @@ -0,0 +1,38 @@ +/* An attempt to make stylesheets less broken on + * pre-2020 browsers like Chrome 67. + * + * Note: browsers newer than Chrome 84 will not need this. + * + * The main thing about those is that they don't support + * the "gap" property in flexboxes, so that has to be polyfilled + * with margin tags. + */ + +@import "common/measurements"; +@import "common/mixins"; + +@define-mixin legacy-flex-gap $classname, $size { + .$(classname) > * { + margin-bottom: $(size); + margin-right: $(size); + } +} + +header > *, nav.header__secondary > * { + margin-left: var(--padding-large); +} + +@mixin if-mobile { + header > * { + margin-left: var(--padding-normal); + } +} + +@mixin legacy-flex-gap block__header__buttons, var(--padding-normal); +@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 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/themes/dark-blue.css b/assets/css/themes/dark-blue.css index 9a3cd1af..199404a4 100644 --- a/assets/css/themes/dark-blue.css +++ b/assets/css/themes/dark-blue.css @@ -4,7 +4,7 @@ $text-color: #e0e0e0; $primary-color: #284371; $secondary-color: #546c99; $danger-color: #6d2a20; -$warning-color: #715227; +$warning-color: #6d421a; $success-color: #25603e; $information-color: #1c606a; $special-color: #65206e; diff --git a/assets/css/themes/dark-purple.css b/assets/css/themes/dark-purple.css new file mode 100644 index 00000000..8c68b41f --- /dev/null +++ b/assets/css/themes/dark-purple.css @@ -0,0 +1,92 @@ +$background-color: #15121a; +$text-color: #e0e0e0; + +$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 { + --background-color: $background-color; + --text-color: $text-color; + --text-light-color: $text-color; + + --link-color: hsl(from $primary-color h calc(s + 10) calc(l + 50)); + --link-hover-color: $text-color; + + --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 - 5)); + --primary-dark-color: hsl(from $primary-color h calc(s - 15) calc(l - 9)); + --primary-link-color: var(--link-color); /* for consistency */ + + --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)); + + --upvote-color: $upvote-color; + --downvote-color: $downvote-color; + --fave-color: $fave-color; + --comment-color: $comment-color; + --hide-color: $hide-color; + + --spoiler-color: $spoiler-color; + --spoiler-revealed-color: hsl(from $spoiler-color h s calc(l + 20)); + + @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; + + @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/views/communication.css b/assets/css/views/communication.css index b00cf338..ee6bb5ba 100644 --- a/assets/css/views/communication.css +++ b/assets/css/views/communication.css @@ -45,3 +45,101 @@ .communication__anonymous--label { padding: 0 var(--padding-small); } + +.communication > .block__content:first-of-type { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.communication > .block__content:last-of-type { + background: var(--primary-muted-color); + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.communication__body { + display: block; + overflow: hidden; +} + +.communication__body__text { + border-radius: var(--border-radius-inner); + word-wrap: break-word; +} + +.communication__sender-name { + display: flex; + align-items: center; + gap: var(--padding-normal); +} + +.communication__sender-block { + display: flex; + flex-direction: column; + background: var(--primary-muted-color); + border-radius: var(--border-radius-inner); + padding: var(--padding-small); +} + +@mixin if-mobile { + .communication__body__text { + margin-top: var(--padding-normal); + } + + .communication__sender-block { + flex-direction: row; + gap: var(--padding-normal); + align-items: center; + } + + .communication__sender-block > .image-constrained { + min-height: var(--avatar-small-size); + min-width: var(--avatar-small-size); + } +} + +.communication__interaction { + display: inline-block; + background: var(--secondary-muted-color); + color: var(--secondary-link-color) !important; + padding: var(--padding-tiny); + border-radius: var(--border-radius-inner); + margin-left: var(--padding-small); +} + +.communication__interaction:first-child { + margin: 0; +} + +.communication__info { + display: inline-block; + background: var(--information-muted-color); + padding: var(--padding-tiny); + border-radius: var(--border-radius-inner); + margin-left: var(--padding-small); +} + +.communication__info > a { + color: var(--information-link-color); +} + +.togglable-delete-form, .togglable-delete-form-link { + margin-top: var(--padding-small) !important; +} + +.togglable-delete-form-link { + background: var(--danger-muted-color); + color: var(--danger-link-color) !important; +} + +.owner-options { + margin-left: var(--padding-small); +} + +@mixin if-mobile { + .communication__options > *:first-child { + display: flex; + flex-direction: column; + gap: var(--padding-small); + } +} diff --git a/assets/css/elements/interaction.css b/assets/css/views/interaction.css similarity index 100% rename from assets/css/elements/interaction.css rename to assets/css/views/interaction.css diff --git a/assets/css/views/markdown.css b/assets/css/views/markdown.css index 9d42aaa4..6897f7e6 100644 --- a/assets/css/views/markdown.css +++ b/assets/css/views/markdown.css @@ -30,3 +30,7 @@ .spoiler-revealed a, .spoiler:hover a { color: var(--link-color); } + +.walloftext { + line-height: var(--readable-line-height); +} diff --git a/assets/css/views/metabar.css b/assets/css/views/metabar.css index 07d36cc5..a6a85444 100644 --- a/assets/css/views/metabar.css +++ b/assets/css/views/metabar.css @@ -4,6 +4,16 @@ } } +.metabar__interactions { + display: block; +} + +.metabar__interactions .comments_count { + background: 0; + border: 0; + position: static; +} + @mixin if-phone { .metabar__interactions { display: grid; @@ -55,7 +65,25 @@ } .metabar__user-credit { + display: flex; box-sizing: border-box; + gap: var(--padding-small); +} + +.metabar__user-credit .image_uploader { + display: flex; + align-self: center; + align-items: center; + vertical-align: center; + gap: var(--padding-small); +} + +/* For some bizarre reason the icon appears to have + * comically large gap on desktop */ +@mixin if-desktop { + .image_uploader > .username-with-icon { + gap: 0; + } } .metabar__mobile-info td { diff --git a/assets/css/views/pagination.css b/assets/css/views/pagination.css index 99dc59a4..0e520482 100644 --- a/assets/css/views/pagination.css +++ b/assets/css/views/pagination.css @@ -12,7 +12,7 @@ font-size: 0.8rem !important; } -.pagination a { +.pagination a, .pagination span { display: grid; grid-template-columns: auto; gap: var(--padding-tiny); diff --git a/assets/css/views/user.css b/assets/css/views/user.css new file mode 100644 index 00000000..fabf7dbe --- /dev/null +++ b/assets/css/views/user.css @@ -0,0 +1,35 @@ +.badges { + display: grid; + grid-template-columns: repeat(6, var(--badge-small-size)); + gap: var(--padding-small); +} + +.badge { + width: var(--badge-small-size); + height: var(--badge-small-size); +} + +.badge__overflow { + background: var(--information-color); + border-radius: var(--border-radius-inner); + text-align: center; + vertical-align: center; + line-height: calc(var(--badge-small-size) * 1.2); + width: calc(var(--badge-small-size) * 1.2); + height: calc(var(--badge-small-size) * 1.2); + font-size: calc(var(--font-size) * 0.8); +} + +.username-with-icon { + display: inline-flex; + gap: var(--padding-tiny); + align-items: center; +} + +.user-title { + display: flex; + flex-direction: row; + gap: var(--padding-small); + flex-wrap: wrap; + margin-top: var(--padding-small); +} diff --git a/assets/js/fingerprint.js b/assets/js/fp.js similarity index 67% rename from assets/js/fingerprint.js rename to assets/js/fp.js index cbc27c3a..e84e5b98 100644 --- a/assets/js/fingerprint.js +++ b/assets/js/fp.js @@ -1,5 +1,5 @@ /** - * Fingerprints + * Thanks uBlock for breaking our JS! */ // http://stackoverflow.com/a/34842797 @@ -8,7 +8,7 @@ function hashCode(str) { ((prevHash << 5) - prevHash) + currVal.charCodeAt(0), 0) >>> 0; } -function createFingerprint() { +function createFp() { const prints = [ navigator.userAgent, navigator.cpuClass, @@ -33,19 +33,19 @@ function createFingerprint() { return hashCode(prints.join('')); } -function setFingerprintCookie() { - let fingerprint; +function setFpCookie() { + let fp; // The prepended 'c' acts as a crude versioning mechanism. try { - fingerprint = `c${createFingerprint()}`; + fp = `c${createFp()}`; } - // If fingerprinting fails, use fakeprint "c1836832948" as a last resort. + // If it fails, use fakeprint "c1836832948" as a last resort. catch (err) { - fingerprint = 'c1836832948'; + fp = 'c1836832948'; } - document.cookie = `_ses=${fingerprint}; path=/; SameSite=Lax`; + document.cookie = `_ses=${fp}; path=/; SameSite=Lax`; } -export { setFingerprintCookie }; +export { setFpCookie }; diff --git a/assets/js/when-ready.js b/assets/js/when-ready.js index 5ea076d2..88476357 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 { setFingerprintCookie } from './fingerprint'; +import { setFpCookie } from './fp'; import { setupGalleryEditing } from './galleries'; import { initImagesClientside } from './imagesclientside'; import { bindImageTarget } from './image_expansion'; @@ -48,7 +48,7 @@ whenReady(() => { initImagesClientside(); setupComments(); setupDupeReports(); - setFingerprintCookie(); + setFpCookie(); setupGalleryEditing(); bindImageTarget(); setupEvents(); diff --git a/assets/package-lock.json b/assets/package-lock.json index 0b2d10b6..ef8d7056 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -2682,29 +2682,6 @@ "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", @@ -2861,18 +2838,6 @@ "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", @@ -3018,15 +2983,6 @@ "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", @@ -3132,18 +3088,6 @@ "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", @@ -3846,33 +3790,6 @@ "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", @@ -3886,21 +3803,6 @@ "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", @@ -4210,17 +4112,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -4393,18 +4284,6 @@ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5254,6 +5133,11 @@ "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/lib/philomena/users/user.ex b/lib/philomena/users/user.ex index 1972241d..b02f9a19 100644 --- a/lib/philomena/users/user.ex +++ b/lib/philomena/users/user.ex @@ -353,7 +353,10 @@ defmodule Philomena.Users.User do :show_sidebar_and_watched_images ]) |> TagList.propagate_tag_list(:watched_tag_list, :watched_tag_ids) - |> validate_inclusion(:theme, ~W(default dark red)) + |> 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) + ) |> validate_inclusion(:images_per_page, 1..50) |> validate_inclusion(:comments_per_page, 1..100) |> validate_inclusion(:scale_large_images, ["false", "partscaled", "true"]) diff --git a/lib/philomena_web/controllers/setting_controller.ex b/lib/philomena_web/controllers/setting_controller.ex index 7c3d6db9..68cf76b9 100644 --- a/lib/philomena_web/controllers/setting_controller.ex +++ b/lib/philomena_web/controllers/setting_controller.ex @@ -56,10 +56,18 @@ defmodule PhilomenaWeb.SettingController do ) end + defp determine_theme(%{"theme" => "light", "light_theme" => name} = attrs) when name != nil, + do: Map.replace(attrs, "theme", name) + + defp determine_theme(%{"dark_theme" => name} = attrs) when name != nil, + do: Map.replace(attrs, "theme", name) + + defp determine_theme(attrs), do: Map.replace(attrs, "theme", "dark-blue") + defp maybe_update_user(conn, nil, _user_params), do: {:ok, conn} defp maybe_update_user(conn, user, user_params) do - case Users.update_settings(user, user_params) do + case Users.update_settings(user, determine_theme(user_params)) do {:ok, _user} -> {:ok, conn} diff --git a/lib/philomena_web/plugs/content_security_policy_plug.ex b/lib/philomena_web/plugs/content_security_policy_plug.ex index 210ff645..c32940f5 100644 --- a/lib/philomena_web/plugs/content_security_policy_plug.ex +++ b/lib/philomena_web/plugs/content_security_policy_plug.ex @@ -70,12 +70,12 @@ defmodule PhilomenaWeb.ContentSecurityPolicyPlug do defp cdn_uri, do: Application.get_env(:philomena, :cdn_host) |> to_uri() defp camo_uri, do: Application.get_env(:philomena, :camo_host) |> to_uri() - defp default_script_src, do: vite_hmr?(do: "'self' localhost:5173", else: "'self'") + defp default_script_src, do: vite_hmr?(do: "*", else: "'self'") defp default_connect_src, - do: vite_hmr?(do: "'self' localhost:5173 ws://localhost:5173", else: "'self'") + do: vite_hmr?(do: "*", else: "'self'") - defp default_style_src, do: vite_hmr?(do: "'self' 'unsafe-inline'", else: "'self'") + defp default_style_src, do: vite_hmr?(do: "*", else: "'self'") defp to_uri(host) when host in [nil, ""], do: "" defp to_uri(host), do: URI.to_string(%URI{scheme: "https", host: host}) diff --git a/lib/philomena_web/templates/admin/report/show.html.slime b/lib/philomena_web/templates/admin/report/show.html.slime index d8cc0972..4b819f94 100644 --- a/lib/philomena_web/templates/admin/report/show.html.slime +++ b/lib/philomena_web/templates/admin/report/show.html.slime @@ -4,14 +4,7 @@ p article.block.communication .block__content.flex.flex--no-wrap - .flex__fixed.spacing--right - = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @report, conn: @conn - .flex__grow.communication__body - span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @report, awards: true, conn: @conn - br - = render PhilomenaWeb.UserAttributionView, "_anon_user_title.html", object: @report, conn: @conn - .communication__body__text - =<> @body + = render PhilomenaWeb.CommunicationView, "_body.html", object: @report, body: @body, conn: @conn .block__content.communication__options .flex.flex--wrap.flex--spaced-out diff --git a/lib/philomena_web/templates/comment/_comment.html.slime b/lib/philomena_web/templates/comment/_comment.html.slime index 341f071d..09f65585 100644 --- a/lib/philomena_web/templates/comment/_comment.html.slime +++ b/lib/philomena_web/templates/comment/_comment.html.slime @@ -22,33 +22,7 @@ article.block.communication id="comment_#{@comment.id}" = submit "Delete", class: "button" .block__content.flex.flex--no-wrap class=communication_body_class(@comment) - .flex__fixed.spacing--right - = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @comment, conn: @conn - .flex__grow.communication__body - span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, awards: true, conn: @conn - br - = render PhilomenaWeb.UserAttributionView, "_anon_user_title.html", object: @comment, conn: @conn - .communication__body__text - = if @comment.hidden_from_users do - strong.comment_deleted - ' Deletion reason: - =<> @comment.deletion_reason - = if can?(@conn, :hide, @comment) and not is_nil(@comment.deleted_by) do - | ( - = @comment.deleted_by.name - | ) - - = if can?(@conn, :hide, @comment) do - = if @comment.destroyed_content do - br - strong.comment_deleted> - | This comment's contents have been destroyed. - - else - br - =<> @body - - - else - =<> @body + = render PhilomenaWeb.CommunicationView, "_body.html", object: @comment, body: @body, conn: @conn, name: "comment" .block__content.communication__options .flex.flex--wrap.flex--spaced-out 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 3e165870..33038f00 100644 --- a/lib/philomena_web/templates/comment/_comment_with_image.html.slime +++ b/lib/philomena_web/templates/comment/_comment_with_image.html.slime @@ -1,37 +1,6 @@ article.block.communication id="comment_#{@comment.id}" .block__content.flex.flex--no-wrap class=communication_body_class(@comment) - .flex__fixed.spacing--right - .post-image-container - = render PhilomenaWeb.ImageView, "_image_container.html", image: @comment.image, size: :thumb_tiny, conn: @conn - - .flex__grow.communication__body - span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @comment, awards: true, conn: @conn - br - - = render PhilomenaWeb.UserAttributionView, "_anon_user_title.html", object: @comment, conn: @conn - - .communication__body__text - = if @comment.hidden_from_users do - strong.comment_deleted - ' Deletion reason: - =<> @comment.deletion_reason - - = if can?(@conn, :hide, @comment) and not is_nil(@comment.deleted_by) do - | ( - = @comment.deleted_by.name - | ) - - = if can?(@conn, :hide, @comment) do - = if @comment.destroyed_content do - br - strong.comment_deleted> - | This comment's contents have been destroyed. - - else - br - =<> @body - - - else - =<> @body + = render PhilomenaWeb.CommunicationView, "_body.html", object: @comment, image: @comment.image, body: @body, conn: @conn, name: "comment" .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 new file mode 100644 index 00000000..d9c69ff0 --- /dev/null +++ b/lib/philomena_web/templates/communication/_body.html.slime @@ -0,0 +1,60 @@ +- anon = is_nil(assigns[:noanon]) or @noanon == false + +- avatar = cond do + - not is_nil(assigns[:image]) -> + .post-image-container + = render PhilomenaWeb.ImageView, "_image_container.html", image: @image, size: :thumb_tiny, conn: @conn + - anon -> + = 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 + = render PhilomenaWeb.UserAttributionView, "_user.html", object: @object, badges: true, conn: @conn + +- title = if anon do + = render PhilomenaWeb.UserAttributionView, "_anon_user_title.html", object: @object, conn: @conn +- 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 + = 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 +- else + =<> @body + +.flex.flex__grow.hidden--mobile + .flex.flex__fixed.spacing--right + = avatar + .flex__grow.communication__body + .communication__sender-block + span.communication__sender-name + = username + = title + .communication__body__text + = contents +.flex.flex__column.flex__grow.hidden--desktop + .communication__sender-block + = avatar + .flex__column.flex--small-gap + span.communication__sender-name + = username + = title + .communication__body.communication__body__text + = contents + \ No newline at end of file diff --git a/lib/philomena_web/templates/error/show.html.slime b/lib/philomena_web/templates/error/show.html.slime index c0256043..46d40238 100644 --- a/lib/philomena_web/templates/error/show.html.slime +++ b/lib/philomena_web/templates/error/show.html.slime @@ -9,7 +9,7 @@ html lang="en" => @status | - Philomena link rel="stylesheet" href=stylesheet_path(@conn, nil) - link rel="stylesheet" href=dark_stylesheet_path(@conn) media="(prefers-color-scheme: dark)" + link rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)" link rel="icon" href="/favicon.ico" type="image/x-icon" link rel="icon" href="/favicon.svg" type="image/svg+xml" diff --git a/lib/philomena_web/templates/image/_image_meta.html.slime b/lib/philomena_web/templates/image/_image_meta.html.slime index 047f4361..5aac5cd5 100644 --- a/lib/philomena_web/templates/image/_image_meta.html.slime +++ b/lib/philomena_web/templates/image/_image_meta.html.slime @@ -61,10 +61,9 @@ a href="#{pretty_url(@image, true, true)}" title="Download (no tags in filename)" i.fa.fa-download .metabar.metabar__user-credit.hidden--phone.layout--centered#extrameta - div - ' Uploaded - => pretty_time(@image.created_at) - = render PhilomenaWeb.ImageView, "_uploader.html", assigns + ' Uploaded + => pretty_time(@image.created_at) + = render PhilomenaWeb.ImageView, "_uploader.html", assigns span.image-size |   diff --git a/lib/philomena_web/templates/image/index.html.slime b/lib/philomena_web/templates/image/index.html.slime index 6426dddd..389fe61b 100644 --- a/lib/philomena_web/templates/image/index.html.slime +++ b/lib/philomena_web/templates/image/index.html.slime @@ -14,7 +14,7 @@ elixir: .block__header.page__header => header - .block.block--borderless.flex__row.flex--spaced-out + .block.block--borderless.flex__row.flex--spaced-out.flex--wrap = if @images.total_pages > 1 do .button__group--standalone .page__pagination = pagination @@ -41,17 +41,15 @@ elixir: - image -> = render PhilomenaWeb.ImageView, "_image_box.html", image: image, link: image_url.(image), size: assigns[:size] || :thumb, conn: @conn - .block.block--borderless.block--spaced-top.flex__row + br + + .block.block--borderless.flex__row.flex--normal-gap.flex--wrap .button__group--standalone .page__pagination = pagination - - span.page__info - = info - - .flex__spacer - .page__info.button__group--standalone a href="/settings/edit" title="Display Settings" i.fa.fa-cog span.hidden--mobile<> ' Display Settings + span.page__info + = info diff --git a/lib/philomena_web/templates/layout/app.html.slime b/lib/philomena_web/templates/layout/app.html.slime index 89c3b1ea..0cbf9366 100644 --- a/lib/philomena_web/templates/layout/app.html.slime +++ b/lib/philomena_web/templates/layout/app.html.slime @@ -13,7 +13,7 @@ html lang="en" link rel="stylesheet" href="/css/application.css" link rel="stylesheet" href=stylesheet_path(@conn, @current_user) = if is_nil(@current_user) do - link rel="stylesheet" href=dark_stylesheet_path(@conn) media="(prefers-color-scheme: dark)" + link rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)" link rel="icon" href="/favicon.ico" type="image/x-icon" link rel="icon" href="/favicon.svg" type="image/svg+xml" meta name="generator" content="philomena" diff --git a/lib/philomena_web/templates/layout/two_factor.html.slime b/lib/philomena_web/templates/layout/two_factor.html.slime index 2d2fc4bb..5b31f3f3 100644 --- a/lib/philomena_web/templates/layout/two_factor.html.slime +++ b/lib/philomena_web/templates/layout/two_factor.html.slime @@ -7,7 +7,7 @@ html lang="en" title Two Factor Authentication - Derpibooru link rel="stylesheet" href=stylesheet_path(@conn, nil) - link rel="stylesheet" href=dark_stylesheet_path(@conn) media="(prefers-color-scheme: dark)" + link rel="stylesheet" href=light_stylesheet_path(@conn) media="(prefers-color-scheme: light)" link rel="icon" href="/favicon.ico" type="image/x-icon" link rel="icon" href="/favicon.svg" type="image/svg+xml" diff --git a/lib/philomena_web/templates/message/_message.html.slime b/lib/philomena_web/templates/message/_message.html.slime index 58e420a3..8522e124 100644 --- a/lib/philomena_web/templates/message/_message.html.slime +++ b/lib/philomena_web/templates/message/_message.html.slime @@ -14,18 +14,7 @@ article.block.communication ' Approve .block__content.flex.flex--no-wrap - .flex__fixed.spacing--right - = render PhilomenaWeb.UserAttributionView, "_user_avatar.html", object: %{user: @message.from}, conn: @conn, class: "avatar--small" - - .flex__grow.communication__body - - span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_user.html", object: %{user: @message.from}, badges: true, conn: @conn - br - - = render PhilomenaWeb.UserAttributionView, "_user_title.html", object: %{user: @message.from}, conn: @conn - - .communication__body__text - = @body + = 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 diff --git a/lib/philomena_web/templates/pagination/_pagination.html.slime b/lib/philomena_web/templates/pagination/_pagination.html.slime index b77f4673..05508188 100644 --- a/lib/philomena_web/templates/pagination/_pagination.html.slime +++ b/lib/philomena_web/templates/pagination/_pagination.html.slime @@ -40,11 +40,11 @@ nav.pagination.hidden--desktop = if first_page?(@page) do - span + span.with-icon i.fa.fa-backward> ' First .separator--vertical.separator--secondary - span + span.with-icon i.fa.fa-chevron-left> ' Prev .separator--vertical.separator--secondary @@ -52,12 +52,14 @@ = link to: first_page_path(@page, @route, params), class: "with-icon" do i.fa.fa-backward> ' First + .separator--vertical.separator--secondary = link to: prev_page_path(@page, @route, params), class: "js-prev with-icon" do i.fa.fa-chevron-left> ' Prev + .separator--vertical.separator--secondary .dropdown - a.page-current.pagination__dropdown + a.with-icon.page-current.pagination__dropdown => @page.page_number i.fa.fa-caret-down @@ -80,11 +82,11 @@ = if last_page?(@page) do .separator--vertical.separator--secondary - span + span.with-icon ' Next i.fa.fa-chevron-right .separator--vertical.separator--secondary - span + span.with-icon ' Last i.fa.fa-fast-forward - else diff --git a/lib/philomena_web/templates/post/_post.html.slime b/lib/philomena_web/templates/post/_post.html.slime index e6b2c112..1826face 100644 --- a/lib/philomena_web/templates/post/_post.html.slime +++ b/lib/philomena_web/templates/post/_post.html.slime @@ -22,33 +22,7 @@ article.block.communication id="post_#{@post.id}" = submit "Delete", class: "button" .block__content.flex.flex--no-wrap class=communication_body_class(@post) - .flex__fixed.spacing--right - = render PhilomenaWeb.UserAttributionView, "_anon_user_avatar.html", object: @post, conn: @conn - .flex__grow.communication__body - span.communication__body__sender-name = render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @post, awards: true, conn: @conn - br - = render PhilomenaWeb.UserAttributionView, "_anon_user_title.html", object: @post, conn: @conn - .communication__body__text - = if @post.hidden_from_users do - strong.comment_deleted - ' Deletion reason: - => @post.deletion_reason - = if can?(@conn, :hide, @post) and not is_nil(@post.deleted_by) do - | ( - = @post.deleted_by.name - | ) - - = if can?(@conn, :hide, @post) do - = if @post.destroyed_content do - br - strong.comment_deleted> - | This post's contents have been destroyed. - - else - br - =<> @body - - - else - =<> @body + = render PhilomenaWeb.CommunicationView, "_body.html", object: @post, body: @body, conn: @conn, name: "post" .block__content.communication__options .flex.flex--wrap.flex--spaced-out diff --git a/lib/philomena_web/templates/profile/_about_me.html.slime b/lib/philomena_web/templates/profile/_about_me.html.slime index 844395ee..55e5d732 100644 --- a/lib/philomena_web/templates/profile/_about_me.html.slime +++ b/lib/philomena_web/templates/profile/_about_me.html.slime @@ -2,6 +2,8 @@ = cond do - @user.description not in [nil, ""] -> = @about_me + - true -> + | No description provided. = if can?(@conn, :edit_description, @user) do = if @user.description not in [nil, ""] do diff --git a/lib/philomena_web/templates/profile/_awards.html.slime b/lib/philomena_web/templates/profile/_awards.html.slime index be7834ec..03d20ace 100644 --- a/lib/philomena_web/templates/profile/_awards.html.slime +++ b/lib/philomena_web/templates/profile/_awards.html.slime @@ -1,6 +1,6 @@ .badges - awards = award_order(@awards) - - {awards, overflow} = Enum.split(awards, 10) + - {awards, overflow} = Enum.split(awards, 5) = for award <- awards do - title = [award_title(award), award.label] |> Enum.join(" - ") @@ -8,11 +8,6 @@ = badge_image(award.badge, alt: title, title: title, width: "18", height: "18") = if Enum.any?(overflow) do - .dropdown - i.fa.fa-caret-down - .dropdown__content.block__header - .badges.flex--column - = for award <- overflow do - - title = [award_title(award), award.label] |> Enum.join(" - ") - .badge - = badge_image(award.badge, alt: title, title: title, width: "18", height: "18") + span.badge__overflow + | + + = Enum.count(overflow) diff --git a/lib/philomena_web/templates/setting/edit.html.slime b/lib/philomena_web/templates/setting/edit.html.slime index 92e13038..cce9bdf3 100644 --- a/lib/philomena_web/templates/setting/edit.html.slime +++ b/lib/philomena_web/templates/setting/edit.html.slime @@ -56,36 +56,54 @@ h1 Content Settings ' Do not share this URL with anyone, it may allow an attacker to compromise your account. .block__tab.hidden.flex.flex--maybe-wrap data-tab="display" - div + .form--three-items .field => label f, :use_centered_layout - => checkbox f, :use_centered_layout, class: "checkbox" + .with-error + => checkbox f, :use_centered_layout, class: "checkbox" .fieldlabel: i Align content to the center of the page - try this option out if you browse the site on a tablet or a fairly wide screen. .field => label f, :show_sidebar_and_watched_images - => checkbox f, :show_sidebar_and_watched_images, class: "checkbox" + .with-error + => checkbox f, :show_sidebar_and_watched_images, class: "checkbox" .fieldlabel: i Show the sidebar and new watched images on the homepage (the default) or hide it. .field => label f, :hide_vote_counts - => checkbox f, :hide_vote_counts, class: "checkbox" + .with-error + => checkbox f, :hide_vote_counts, class: "checkbox" .fieldlabel: i Hide upvote and downvote counts on images, showing only the overall score .field => label f, :images_per_page - => number_input f, :images_per_page, min: 1, max: 50, step: 1, class: "input" - = error_tag f, :images_per_page + .with-error + => number_input f, :images_per_page, min: 1, max: 50, step: 1, class: "input" + = error_tag f, :images_per_page .fieldlabel i ' This is the number of images per page that are displayed on image listings and searches, up to a maximum of 50. ' For 1080p monitors, try 24. .field - => label f, :theme - => select f, :theme, theme_options(@conn), class: "input" - = error_tag f, :theme + label Theme + select.input#js-theme-selector name="user[theme]" id="user_theme" + option value="dark" Dark + option value="light" Light + .fieldlabel: i General appearance of the theme + .field#js-theme-dark + label Theme color + .with-error + => select f, :dark_theme, theme_options(@conn), 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" + = 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 - => select f, :scale_large_images, scale_options(), class: "input" - = error_tag f, :scale_large_images + .with-error + => select f, :scale_large_images, scale_options(), class: "input" + = error_tag f, :scale_large_images .block__tab.hidden.flex.flex--maybe-wrap data-tab="comments" div 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 b27abcb2..f58c6cde 100644 --- a/lib/philomena_web/templates/user_attribution/_anon_user.html.slime +++ b/lib/philomena_web/templates/user_attribution/_anon_user.html.slime @@ -1,6 +1,9 @@ = cond do - not is_nil(@object.user) and not anonymous?(@object) -> - strong<> + strong.username-with-icon<> + - icon = user_icon(@object.user) + = if icon do + i class="fa #{icon}" = link(@object.user.name, to: Routes.profile_path(@conn, :show, @object.user)) = if assigns[:awards] do = render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards diff --git a/lib/philomena_web/templates/user_attribution/_anon_user_title.html.slime b/lib/philomena_web/templates/user_attribution/_anon_user_title.html.slime index 2d0a6d45..4173992b 100644 --- a/lib/philomena_web/templates/user_attribution/_anon_user_title.html.slime +++ b/lib/philomena_web/templates/user_attribution/_anon_user_title.html.slime @@ -1,6 +1,7 @@ = if !!@object.user and !anonymous?(@object) do - = for {class, label} <- user_labels(@object) do - = if assigns[:large] do - .label.label--block class=class = label - - else - .label.label--block.label--small class=class = label \ No newline at end of file + .user-title + = for {class, label} <- user_labels(@object) do + = if assigns[:large] do + .label class=class = label + - else + .label.label--small class=class = label diff --git a/lib/philomena_web/templates/user_attribution/_user.html.slime b/lib/philomena_web/templates/user_attribution/_user.html.slime index e58b85a4..63013ac1 100644 --- a/lib/philomena_web/templates/user_attribution/_user.html.slime +++ b/lib/philomena_web/templates/user_attribution/_user.html.slime @@ -1,5 +1,8 @@ = if !!@object.user do strong<> + - icon = user_icon(@object.user) + = if icon do + i class="fa #{icon}"> = link(@object.user.name, to: Routes.profile_path(@conn, :show, @object.user)) = if assigns[:awards] do - = render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards \ No newline at end of file + = render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards diff --git a/lib/philomena_web/templates/user_attribution/_user_title.html.slime b/lib/philomena_web/templates/user_attribution/_user_title.html.slime index 1dba6410..0d4f361f 100644 --- a/lib/philomena_web/templates/user_attribution/_user_title.html.slime +++ b/lib/philomena_web/templates/user_attribution/_user_title.html.slime @@ -1,5 +1,6 @@ -= for {class, label} <- user_labels(@object) do - = if assigns[:large] do - .label.label--block class=class = label - - else - .label.label--block.label--small class=class = label +.user-title + = for {class, label} <- user_labels(@object) do + = if assigns[:large] do + .label class=class = label + - else + .label.label--small class=class = label diff --git a/lib/philomena_web/views/admin/user_view.ex b/lib/philomena_web/views/admin/user_view.ex index dec77bee..11d90b98 100644 --- a/lib/philomena_web/views/admin/user_view.ex +++ b/lib/philomena_web/views/admin/user_view.ex @@ -46,7 +46,9 @@ defmodule PhilomenaWeb.Admin.UserView do def description("moderator", "Tag"), do: "Manage tag details" def description("admin", "Tag"), do: "Alias tags" - def description("batch_update", "Tag"), do: "Update tags in batches (do not issue to staff members)" + + def description("batch_update", "Tag"), + do: "Update tags in batches (do not issue to staff members)" def description("moderator", "User"), do: "Manage users and wipe votes" def description("admin", "Role"), do: "Manage permissions" diff --git a/lib/philomena_web/views/communication/communication_view.ex b/lib/philomena_web/views/communication/communication_view.ex new file mode 100644 index 00000000..f88e977c --- /dev/null +++ b/lib/philomena_web/views/communication/communication_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.CommunicationView do + use PhilomenaWeb, :view +end diff --git a/lib/philomena_web/views/error_view.ex b/lib/philomena_web/views/error_view.ex index 8bf8c1d0..f82bd265 100644 --- a/lib/philomena_web/views/error_view.ex +++ b/lib/philomena_web/views/error_view.ex @@ -4,7 +4,7 @@ defmodule PhilomenaWeb.ErrorView do import PhilomenaWeb.LayoutView, only: [ stylesheet_path: 2, - dark_stylesheet_path: 1, + light_stylesheet_path: 1, viewport_meta_tag: 1 ] diff --git a/lib/philomena_web/views/layout_view.ex b/lib/philomena_web/views/layout_view.ex index 256059c6..c6acf718 100644 --- a/lib/philomena_web/views/layout_view.ex +++ b/lib/philomena_web/views/layout_view.ex @@ -69,17 +69,32 @@ defmodule PhilomenaWeb.LayoutView do Config.get(:footer) end - # def stylesheet_path(conn, %{theme: "dark"}), - # do: Routes.static_path(conn, "/css/dark.css") - - # def stylesheet_path(conn, %{theme: "red"}), - # do: Routes.static_path(conn, "/css/red.css") + def stylesheet_path(conn, %{theme: theme}) + when theme in [ + "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" + ], + do: Routes.static_path(conn, "/css/#{theme}.css") def stylesheet_path(conn, _user), do: Routes.static_path(conn, "/css/dark-blue.css") - def dark_stylesheet_path(conn), - do: Routes.static_path(conn, "/css/dark-blue.css") + def light_stylesheet_path(conn), + do: Routes.static_path(conn, "/css/light-blue.css") def theme_name(%{theme: theme}), do: theme def theme_name(_user), do: "default" diff --git a/lib/philomena_web/views/setting_view.ex b/lib/philomena_web/views/setting_view.ex index df313385..4ed66c29 100644 --- a/lib/philomena_web/views/setting_view.ex +++ b/lib/philomena_web/views/setting_view.ex @@ -4,12 +4,90 @@ defmodule PhilomenaWeb.SettingView do def theme_options(conn) do [ [ - key: "Default", - value: "default", - data: [theme_path: Routes.static_path(conn, "/css/default.css")] + key: "Blue (default)", + value: "dark-blue", + data: [theme_path: Routes.static_path(conn, "/css/dark-blue.css")] ], - [key: "Dark", value: "dark", data: [theme_path: Routes.static_path(conn, "/css/dark.css")]], - [key: "Red", value: "red", data: [theme_path: Routes.static_path(conn, "/css/red.css")]] + [ + key: "Red", + value: "dark-red", + data: [theme_path: Routes.static_path(conn, "/css/dark-red.css")] + ], + [ + key: "Green", + value: "dark-green", + data: [theme_path: Routes.static_path(conn, "/css/dark-green.css")] + ], + [ + key: "Purple", + value: "dark-purple", + data: [theme_path: Routes.static_path(conn, "/css/dark-purple.css")] + ], + [ + key: "Pink", + value: "dark-pink", + data: [theme_path: Routes.static_path(conn, "/css/dark-pink.css")] + ], + [ + key: "Yellow", + value: "dark-yellow", + data: [theme_path: Routes.static_path(conn, "/css/dark-yellow.css")] + ], + [ + key: "Cyan", + value: "dark-cyan", + data: [theme_path: Routes.static_path(conn, "/css/dark-cyan.css")] + ], + [ + key: "Grey", + value: "dark-grey", + data: [theme_path: Routes.static_path(conn, "/css/dark-grey.css")] + ] + ] + end + + def light_theme_options(conn) do + [ + [ + key: "Blue (default)", + value: "light-blue", + data: [theme_path: Routes.static_path(conn, "/css/light-blue.css")] + ], + [ + key: "Red", + value: "light-red", + data: [theme_path: Routes.static_path(conn, "/css/light-red.css")] + ], + [ + key: "Green", + value: "light-green", + data: [theme_path: Routes.static_path(conn, "/css/light-green.css")] + ], + [ + key: "Purple", + value: "light-purple", + data: [theme_path: Routes.static_path(conn, "/css/light-purple.css")] + ], + [ + key: "Pink", + value: "light-pink", + data: [theme_path: Routes.static_path(conn, "/css/light-pink.css")] + ], + [ + key: "Yellow", + value: "light-yellow", + data: [theme_path: Routes.static_path(conn, "/css/light-yellow.css")] + ], + [ + key: "Cyan", + value: "light-cyan", + data: [theme_path: Routes.static_path(conn, "/css/light-cyan.css")] + ], + [ + key: "Grey", + value: "light-grey", + data: [theme_path: Routes.static_path(conn, "/css/light-grey.css")] + ] ] end diff --git a/lib/philomena_web/views/user_attribution_view.ex b/lib/philomena_web/views/user_attribution_view.ex index b28fcb4c..24ae18f7 100644 --- a/lib/philomena_web/views/user_attribution_view.ex +++ b/lib/philomena_web/views/user_attribution_view.ex @@ -81,6 +81,13 @@ defmodule PhilomenaWeb.UserAttributionView do "data:image/svg+xml;base64," <> Base.encode64(svg) end + def user_icon(%{secondary_role: sr}) when sr in ["Site Developer", "Devops"], do: "fa-screwdriver-wrench" + def user_icon(%{secondary_role: sr}) when sr in ["Public Relations"], do: "fa-bullhorn" + def user_icon(%{hide_default_role: true}), do: nil + def user_icon(%{role: role}) when role in ["admin", "moderator"], do: "fa-gavel" + def user_icon(%{role: "assistant"}), do: "fa-handshake-angle" + def user_icon(_), do: nil + def user_labels(%{user: user}) do [] |> personal_title(user)