forked from e621ng/e621ng
[Users] Rework the profile page (#931)
This commit is contained in:
parent
e304030e8a
commit
0dd17474aa
@ -121,6 +121,10 @@ module ApplicationHelper
|
||||
time_tag(time.strftime("%Y-%m-%d %H:%M"), time)
|
||||
end
|
||||
|
||||
def compact_date(time)
|
||||
time_tag(time.strftime("%Y-%m-%d"), time)
|
||||
end
|
||||
|
||||
def external_link_to(url, truncate: nil, strip_scheme: false, link_options: {})
|
||||
text = url
|
||||
text = text.gsub(%r!\Ahttps?://!i, "") if strip_scheme
|
||||
@ -189,21 +193,6 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
def simple_avatar(user, **options)
|
||||
return "" if user.nil?
|
||||
post_id = user.avatar_id
|
||||
deferred_post_ids.add(post_id) if post_id
|
||||
|
||||
klass = options.delete(:class)
|
||||
named = options.delete(:named)
|
||||
tag.a href: user_path(user), class: "simple-avatar placeholder #{klass}", data: { id: post_id, name: user.name } do
|
||||
tag.span(class: "simple-avatar-button") do
|
||||
concat tag.span(user.pretty_name, class: "simple-avatar-name") if named
|
||||
concat tag.span(class: "simple-avatar-image", data: { name: user.name[0].capitalize })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unread_dmails(user)
|
||||
if user.has_mail?
|
||||
"(#{user.unread_dmail_count})"
|
||||
|
@ -20,8 +20,18 @@ module IconHelper
|
||||
user: %(<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>),
|
||||
|
||||
# Utility
|
||||
plus: %(<path d="M5 12h14"/><path d="M12 5v14"/>),
|
||||
times: %(<path d="M18 6 6 18"/><path d="m6 6 12 12"/>),
|
||||
reset: %(<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/>),
|
||||
replace: %(<path d="M14 4a2 2 0 0 1 2-2"/><path d="M16 10a2 2 0 0 1-2-2"/><path d="M20 2a2 2 0 0 1 2 2"/><path d="M22 8a2 2 0 0 1-2 2"/><path d="m3 7 3 3 3-3"/><path d="M6 10V5a3 3 0 0 1 3-3h1"/><rect x="2" y="14" width="8" height="8" rx="2"/>),
|
||||
upload: %(<path d="M12 13v8"/><path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242"/><path d="m8 17 4-4 4 4"/>),
|
||||
stamp: %(<path d="M19.27 13.73A2.5 2.5 0 0 0 17.5 13h-11A2.5 2.5 0 0 0 4 15.5V17a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-1.5c0-.66-.26-1.3-.73-1.77Z"/><path d="M14 13V8.5C14 7 15 7 15 5a3 3 0 0 0-3-3c-1.66 0-3 1-3 3s1 2 1 3.5V13"/>),
|
||||
power: %(<path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.77.04"/>),
|
||||
circle_help: %(<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/>),
|
||||
notepad: %(<path d="M8 2v4"/><path d="M12 2v4"/><path d="M16 2v4"/><path d="M16 4h2a2 2 0 0 1 2 2v2"/><path d="M20 12v2"/><path d="M20 18v2a2 2 0 0 1-2 2h-1"/><path d="M13 22h-2"/><path d="M7 22H6a2 2 0 0 1-2-2v-2"/><path d="M4 14v-2"/><path d="M4 8V6a2 2 0 0 1 2-2h2"/><path d="M8 10h6"/><path d="M8 14h8"/><path d="M8 18h5"/>),
|
||||
flag_left: %(<path d="M17 22V2L7 7l10 5"/>),
|
||||
ticket: %(<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>),
|
||||
key_square: %(<path d="M12.4 2.7a2.5 2.5 0 0 1 3.4 0l5.5 5.5a2.5 2.5 0 0 1 0 3.4l-3.7 3.7a2.5 2.5 0 0 1-3.4 0L8.7 9.8a2.5 2.5 0 0 1 0-3.4z"/><path d="m14 7 3 3"/><path d="m9.4 10.6-6.814 6.814A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814"/>),
|
||||
|
||||
# Pagination
|
||||
chevron_left: %(<path d="m15 18-6-6 6-6"/>),
|
||||
|
@ -11,4 +11,47 @@ module UsersHelper
|
||||
domain = email.split("@").last
|
||||
link_to "»", users_path(search: { email_matches: "*@#{domain}" })
|
||||
end
|
||||
|
||||
def simple_avatar(user, **options)
|
||||
return "" if user.nil?
|
||||
post_id = user.avatar_id
|
||||
deferred_post_ids.add(post_id) if post_id
|
||||
|
||||
klass = options.delete(:class)
|
||||
named = options.delete(:named)
|
||||
tag.a href: user_path(user), class: "simple-avatar placeholder #{klass}", data: { id: post_id, name: user.name } do
|
||||
tag.span(class: "avatar-button") do
|
||||
concat tag.span(user.pretty_name, class: "avatar-name") if named
|
||||
concat tag.span(class: "avatar-image", data: { name: user.name[0].capitalize })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def profile_avatar(user, **options)
|
||||
return if user.nil?
|
||||
post_id = user.avatar_id
|
||||
deferred_post_ids.add(post_id) if post_id
|
||||
|
||||
klass = options.delete(:class)
|
||||
|
||||
render "/application/profile_avatar", user: user, post_id: post_id, klass: klass
|
||||
end
|
||||
|
||||
def user_level_badge(user)
|
||||
return if user.nil?
|
||||
|
||||
tag.span(class: "level-badge level-#{user.level_string.downcase}") do
|
||||
user.level_string.upcase
|
||||
end
|
||||
end
|
||||
|
||||
def user_feedback_badge(user)
|
||||
return if user.nil?
|
||||
|
||||
feedbacks = user.feedback_pieces
|
||||
deleted = CurrentUser.user.is_staff? ? feedbacks[:deleted] : 0
|
||||
active = feedbacks[:positive] + feedbacks[:neutral] + feedbacks[:negative]
|
||||
|
||||
render "/application/feedback_badge", user: user, positive: feedbacks[:positive], neutral: feedbacks[:neutral], negative: feedbacks[:negative], deleted: deleted, active: active
|
||||
end
|
||||
end
|
||||
|
@ -9,7 +9,7 @@ Thumbnails.initialize = function () {
|
||||
const replacedPosts = [];
|
||||
|
||||
// Avatar special case
|
||||
for (const post of $(".simple-avatar.placeholder")) {
|
||||
for (const post of $(".simple-avatar.placeholder, .profile-avatar.placeholder")) {
|
||||
const $post = $(post);
|
||||
$post.removeClass("placeholder");
|
||||
|
||||
@ -21,7 +21,11 @@ Thumbnails.initialize = function () {
|
||||
|
||||
$("<img>")
|
||||
.attr("src", postData["preview_url"])
|
||||
.appendTo($post.find("span.simple-avatar-image"));
|
||||
.appendTo($post.find("span.avatar-image"));
|
||||
|
||||
if ($post.hasClass("profile-avatar"))
|
||||
$post.attr("href", "/posts/" + postID);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
25
app/javascript/src/javascripts/users.js
Normal file
25
app/javascript/src/javascripts/users.js
Normal file
@ -0,0 +1,25 @@
|
||||
import LStorage from "./utility/storage";
|
||||
|
||||
const Users = {};
|
||||
|
||||
Users.init_section = function ($wrapper) {
|
||||
const $header = $wrapper.find(".profile-section-header").first();
|
||||
const $body = $(".profile-section-body").first();
|
||||
const name = $wrapper.attr("name");
|
||||
if (!name || !$header.length || !$body.length) return;
|
||||
|
||||
let state = LStorage.Users[name];
|
||||
if (state) $wrapper.removeClass("hidden");
|
||||
|
||||
$header.on("click", () => {
|
||||
$wrapper.toggleClass("hidden", state);
|
||||
|
||||
state = !state;
|
||||
LStorage.Users[name] = state;
|
||||
});
|
||||
};
|
||||
|
||||
$(() => {
|
||||
for (const one of $((".profile-section")))
|
||||
Users.init_section($((one)));
|
||||
});
|
@ -190,6 +190,17 @@ LStorage.Blacklist = {
|
||||
StorageUtils.bootstrapSome(LStorage.Blacklist, ["Collapsed"]);
|
||||
|
||||
|
||||
// Users page config
|
||||
LStorage.Users = {
|
||||
/** @returns {boolean} True to show staff stats, false to hide them */
|
||||
StaffStats: ["e6.users.staffstats", false],
|
||||
|
||||
/** @returns {boolean} True to show user stats, false to hide them */
|
||||
StaffNotes: ["e6.users.staffnotes", false],
|
||||
};
|
||||
StorageUtils.bootstrapMany(LStorage.Users);
|
||||
|
||||
|
||||
/**
|
||||
* Patches the add, delete, and clear methods for the filter cache set.
|
||||
* Otherwise, modifying the set with these methods would not update the local storage
|
||||
|
@ -43,6 +43,7 @@
|
||||
@import "common/user_styles.scss";
|
||||
@import "common/voting.scss";
|
||||
|
||||
@import "views/application/application";
|
||||
@import "views/posts/posts";
|
||||
@import "views/users/users";
|
||||
|
||||
|
@ -17,5 +17,5 @@ $st-values: (
|
||||
@function padding($value) { @return st-value(($value)); }
|
||||
@mixin st-padding($value) { padding: padding(($value)); }
|
||||
|
||||
@function radius($value) { @return st-value($value); }
|
||||
@mixin st-radius($value) { border-radius: radius($value); }
|
||||
@function radius($value: 025) { @return st-value($value); }
|
||||
@mixin st-radius($value: 025) { border-radius: radius($value); }
|
||||
|
@ -76,11 +76,11 @@ nav.navigation {
|
||||
}
|
||||
|
||||
a.simple-avatar {
|
||||
.simple-avatar-button {
|
||||
.avatar-button {
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
|
||||
.simple-avatar-name {
|
||||
.avatar-name {
|
||||
padding: 0.5rem;
|
||||
|
||||
@include window-smaller-than(32rem) {
|
||||
@ -88,7 +88,7 @@ nav.navigation {
|
||||
}
|
||||
}
|
||||
|
||||
.simple-avatar-image {
|
||||
.avatar-image {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -119,7 +119,7 @@ nav.navigation {
|
||||
}
|
||||
|
||||
@include window-smaller-than(32rem) {
|
||||
&.sign-in .simple-avatar-image {
|
||||
&.sign-in .avatar-image {
|
||||
background: themed("color-foreground");
|
||||
}
|
||||
}
|
||||
@ -495,18 +495,18 @@ nav.navigation, html.nav-toggled nav.navigation {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
|
||||
.simple-avatar-button {
|
||||
.avatar-button {
|
||||
background: none;
|
||||
color: inherit;
|
||||
align-items: start;
|
||||
font-size: 0.875rem;
|
||||
line-height: 0.875rem;
|
||||
|
||||
.simple-avatar-name {
|
||||
.avatar-name {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.simple-avatar-image {
|
||||
.avatar-image {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
background: themed("color-foreground");
|
||||
@ -528,9 +528,9 @@ nav.navigation, html.nav-toggled nav.navigation {
|
||||
}
|
||||
|
||||
// Stage 2: account label
|
||||
.collapse-2 .simple-avatar-name { display: none; }
|
||||
.collapse-2 .avatar-name { display: none; }
|
||||
@include window-larger-than(65rem) {
|
||||
.collapse-2 .simple-avatar-name { display: unset; }
|
||||
.collapse-2 .avatar-name { display: unset; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -564,10 +564,10 @@ body.c-static.a-home {
|
||||
position: static;
|
||||
height: unset;
|
||||
padding: 0;
|
||||
.simple-avatar-button {
|
||||
.avatar-button {
|
||||
height: unset;
|
||||
align-items: center;
|
||||
.simple-avatar-image { display: none; }
|
||||
.avatar-image { display: none; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ div#c-users {
|
||||
gap: 1em;
|
||||
|
||||
& > div {
|
||||
max-width: 1600px;
|
||||
max-width: 100rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@ -76,107 +76,7 @@ div#c-users {
|
||||
// Middle section: uploads and favorites
|
||||
.blacklist-ui { padding: 0; }
|
||||
|
||||
.posts-section {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
gap: 1em;
|
||||
|
||||
.profile-sample {
|
||||
display: grid;
|
||||
grid-template: "p-header"
|
||||
"p-links"
|
||||
"p-posts";
|
||||
gap: 0.5em 0;
|
||||
|
||||
@include window-larger-than(800px) {
|
||||
grid-template: "p-header p-links"
|
||||
"p-posts p-posts";
|
||||
grid-template-columns: 12em 1fr;
|
||||
gap: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-header a, .profile-sample-links a {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
|
||||
height: 100%;
|
||||
padding: 0.5em;
|
||||
border-radius: 6px;
|
||||
background-color: themed("color-section");
|
||||
|
||||
&:hover {
|
||||
background-color: themed("color-section-lighten-5");
|
||||
}
|
||||
&:focus, &:active {
|
||||
outline: 0;
|
||||
color: themed("color-link-active");
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-header {
|
||||
grid-area: p-header;
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
font-size: 1.25em;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include window-larger-than(800px) {
|
||||
a { border-radius: 6px 6px 0 0; }
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-links {
|
||||
grid-area: p-links;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
padding: 0.5em;
|
||||
color: themed("color-text-muted");
|
||||
}
|
||||
|
||||
.spacer { display: none; }
|
||||
.profile-comments-link {
|
||||
grid-row: 1 / 3;
|
||||
grid-column: 3;
|
||||
}
|
||||
|
||||
@include window-larger-than(800px) {
|
||||
display: flex;
|
||||
a { height: min-content; }
|
||||
.spacer { display: block; }
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-posts {
|
||||
grid-area: p-posts;
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
gap: 1em;
|
||||
padding: 0.5em;
|
||||
|
||||
background: var(--color-section);
|
||||
border-radius: 6px;
|
||||
|
||||
// Desktop
|
||||
@include window-larger-than(800px) {
|
||||
flex-wrap: nowrap;
|
||||
border-top-left-radius: 0;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Bottom section: about me and commission info
|
||||
|
@ -0,0 +1,3 @@
|
||||
@import "avatar";
|
||||
@import "feedback_badge";
|
||||
@import "level_badge";
|
41
app/javascript/src/styles/views/application/_avatar.scss
Normal file
41
app/javascript/src/styles/views/application/_avatar.scss
Normal file
@ -0,0 +1,41 @@
|
||||
.profile-avatar {
|
||||
.avatar-image {
|
||||
display: flex;
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
|
||||
position: relative;
|
||||
|
||||
background: themed("color-section");
|
||||
@include st-radius;
|
||||
|
||||
// Letter if no avatar image
|
||||
&::after {
|
||||
content: attr(data-initial);
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
font-size: 5rem;
|
||||
font-weight: bold;
|
||||
color: themed("color-foreground");
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
// On top of the letter
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: 1;
|
||||
|
||||
@include st-radius;
|
||||
}
|
||||
}
|
||||
}
|
103
app/javascript/src/styles/views/application/_feedback_badge.scss
Normal file
103
app/javascript/src/styles/views/application/_feedback_badge.scss
Normal file
@ -0,0 +1,103 @@
|
||||
$hex-size: 1.25rem;
|
||||
|
||||
.user-record {
|
||||
display: flex;
|
||||
|
||||
height: $hex-size * 0.6; // 0.75rem
|
||||
width: $hex-size; // 1.25rem
|
||||
margin: ($hex-size / 5) 0; // 0.25rem 0
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: ($hex-size / 5 * 4) * 0.9;
|
||||
svg { width: ($hex-size / 5 * 4) * 0.9; }
|
||||
|
||||
background: palette("plain-black");
|
||||
color: white;
|
||||
|
||||
// Place corners under the text
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
&::before, &::after {
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
|
||||
border-left: ($hex-size / 2) solid transparent;
|
||||
border-right: ($hex-size / 2) solid transparent;
|
||||
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::before {
|
||||
top: -($hex-size / 5);
|
||||
border-bottom: ($hex-size / 5) solid palette("plain-black");
|
||||
}
|
||||
|
||||
&::after {
|
||||
bottom: -($hex-size / 5);
|
||||
border-top: ($hex-size / 5) solid palette("plain-black");
|
||||
}
|
||||
|
||||
|
||||
// Variations
|
||||
&.deleted {
|
||||
background: palette("background-yellow");
|
||||
&::before { border-bottom-color: palette("background-yellow"); }
|
||||
&::after { border-top-color: palette("background-yellow"); }
|
||||
}
|
||||
|
||||
&.negative {
|
||||
background: palette("background-red");
|
||||
&::before { border-bottom-color: palette("background-red"); }
|
||||
&::after { border-top-color: palette("background-red"); }
|
||||
}
|
||||
|
||||
&.neutral {
|
||||
background: palette("background-grey");
|
||||
&::before { border-bottom-color: palette("background-grey"); }
|
||||
&::after { border-top-color: palette("background-grey"); }
|
||||
}
|
||||
|
||||
&.positive {
|
||||
background: palette("background-green");
|
||||
&::before { border-bottom-color: palette("background-green"); }
|
||||
&::after { border-top-color: palette("background-green"); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.user-records-list {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
gap: 0.25rem;
|
||||
|
||||
&:hover .user-record {
|
||||
&.deleted {
|
||||
background: palette("background-yellow-d5");
|
||||
&::before { border-bottom-color: palette("background-yellow-d5"); }
|
||||
&::after { border-top-color: palette("background-yellow-d5"); }
|
||||
}
|
||||
|
||||
&.negative {
|
||||
background: palette("background-red-d5");
|
||||
&::before { border-bottom-color: palette("background-red-d5"); }
|
||||
&::after { border-top-color: palette("background-red-d5"); }
|
||||
}
|
||||
|
||||
&.neutral {
|
||||
background: palette("background-grey-d5");
|
||||
&::before { border-bottom-color: palette("background-grey-d5"); }
|
||||
&::after { border-top-color: palette("background-grey-d5"); }
|
||||
}
|
||||
|
||||
&.positive {
|
||||
background: palette("background-green-d5");
|
||||
&::before { border-bottom-color: palette("background-green-d5"); }
|
||||
&::after { border-top-color: palette("background-green-d5"); }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
$user-levels: (
|
||||
"unactivated"
|
||||
"blocked"
|
||||
"member"
|
||||
"privileged"
|
||||
"former-staff"
|
||||
"janitor"
|
||||
"moderator"
|
||||
"admin"
|
||||
);
|
||||
$user-level-text: (
|
||||
"moderator": #ffffff,
|
||||
"janitor": #ffffff,
|
||||
);
|
||||
|
||||
.level-badge {
|
||||
font-weight: bold;
|
||||
font-size: 0.65rem;
|
||||
color: black;
|
||||
|
||||
background: themed("color-user-member");
|
||||
padding: 0.1rem 0.25rem;
|
||||
@include st-radius;
|
||||
|
||||
@each $level in $user-levels {
|
||||
&.level-#{$level} {
|
||||
background: themed("color-user-" + $level);
|
||||
}
|
||||
}
|
||||
@each $level, $color in $user-level-text {
|
||||
&.level-#{$level} {
|
||||
color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
&.level-unactivated {
|
||||
background: themed("palette-background-red");
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
@import "show/show";
|
||||
@import "edit/edit";
|
||||
|
@ -106,8 +106,9 @@ tabs-content {
|
||||
}
|
||||
|
||||
tab-entry {
|
||||
display: none;
|
||||
&.active { display: grid; }
|
||||
display: none !important;
|
||||
&.active { display: grid !important; }
|
||||
&.flex.active { display: flex !important; }
|
||||
grid-template-areas: "head" "body" "hint";
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
|
10
app/javascript/src/styles/views/users/show/_show.scss
Normal file
10
app/javascript/src/styles/views/users/show/_show.scss
Normal file
@ -0,0 +1,10 @@
|
||||
body.c-users.a-show {
|
||||
|
||||
@import "partials/about";
|
||||
@import "partials/ban_banner";
|
||||
@import "partials/card";
|
||||
@import "partials/post_summary";
|
||||
@import "partials/profile_section";
|
||||
@import "partials/staff_info";
|
||||
@import "partials/user_info";
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
tabs-menu, tabs-content {
|
||||
max-width: 100rem;
|
||||
}
|
||||
|
||||
#profile-tabs[data-has-about="false"][data-has-artinfo="false"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.profile-about-section, .profile-artinfo-section {
|
||||
flex-flow: column;
|
||||
align-items: start;
|
||||
|
||||
tab-head {
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include window-larger-than(50rem) {
|
||||
tabs-menu { display: none; }
|
||||
tabs-content {
|
||||
display: grid;
|
||||
grid-template-columns: 15rem 1fr;
|
||||
grid-template-rows: min-content min-content;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.profile-user-info {
|
||||
display: grid !important;
|
||||
grid-template-areas: unset;
|
||||
grid-row: 1 / 4;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
.profile-about-section {
|
||||
display: flex !important;
|
||||
grid-column: 2 / -1;
|
||||
}
|
||||
|
||||
.profile-artinfo-section {
|
||||
display: flex !important;
|
||||
grid-column: 2 / -1;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
.profile-ban {
|
||||
background: var(--palette-background-red);
|
||||
padding: 0.5rem;
|
||||
@include st-radius;
|
||||
|
||||
.styled-dtext p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
.profile-card {
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
gap: 1rem;
|
||||
|
||||
// Info block
|
||||
.profile-info {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
color: themed("color-text");
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-joined {
|
||||
font-size: 0.75rem;
|
||||
color: themed("color-text-muted");
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-quickie {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
|
||||
margin-top: 1rem;
|
||||
|
||||
.entry {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.entry > a {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr;
|
||||
grid-template-areas:
|
||||
"icon header"
|
||||
"icon title ";
|
||||
column-gap: 0.25rem;
|
||||
align-items: center;
|
||||
|
||||
width: 7rem;
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
|
||||
@include st-radius;
|
||||
background: themed("color-section");
|
||||
|
||||
&:hover { background: themed("color-section-lighten-5"); }
|
||||
|
||||
.entry-icon {
|
||||
grid-area: icon;
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
color: themed("color-link-active");
|
||||
}
|
||||
.entry-header {
|
||||
grid-area: header;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.1rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.entry-title {
|
||||
grid-area: title;
|
||||
font-size: 0.9rem;
|
||||
line-height: 0.9rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.entry-extra {
|
||||
a, a:visited { color: themed("color-text-muted"); }
|
||||
a:hover, a:active { color: themed("color-link-active"); }
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
.posts-section {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
gap: 1em;
|
||||
|
||||
// Hack to fix spacing on mobile
|
||||
margin-top: 1rem;
|
||||
@include window-larger-than(50rem) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.profile-sample {
|
||||
display: grid;
|
||||
grid-template: "p-header"
|
||||
"p-links"
|
||||
"p-posts";
|
||||
gap: 0.5em 0;
|
||||
|
||||
@include window-larger-than(50rem) {
|
||||
grid-template: "p-header p-links"
|
||||
"p-posts p-posts";
|
||||
grid-template-columns: 12em 1fr;
|
||||
gap: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-header a, .profile-sample-links a {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
|
||||
height: 100%;
|
||||
padding: 0.5em;
|
||||
border-radius: 6px;
|
||||
background-color: themed("color-section");
|
||||
|
||||
&:hover {
|
||||
background-color: themed("color-section-lighten-5");
|
||||
}
|
||||
&:focus, &:active {
|
||||
outline: 0;
|
||||
color: themed("color-link-active");
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-header {
|
||||
grid-area: p-header;
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
font-size: 1.25em;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@include window-larger-than(800px) {
|
||||
a { border-radius: 6px 6px 0 0; }
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-links {
|
||||
grid-area: p-links;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
padding: 0.5em;
|
||||
color: themed("color-text-muted");
|
||||
}
|
||||
|
||||
.spacer { display: none; }
|
||||
.profile-comments-link {
|
||||
grid-row: 1 / 3;
|
||||
grid-column: 3;
|
||||
}
|
||||
|
||||
@include window-larger-than(50rem) {
|
||||
display: flex;
|
||||
a { height: min-content; }
|
||||
.spacer { display: block; }
|
||||
}
|
||||
}
|
||||
|
||||
.profile-sample-posts {
|
||||
grid-area: p-posts;
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
gap: 1em;
|
||||
padding: 0.5em;
|
||||
|
||||
background: var(--color-section);
|
||||
border-radius: 6px;
|
||||
|
||||
// Desktop
|
||||
@include window-larger-than(50rem) {
|
||||
flex-wrap: nowrap;
|
||||
border-top-left-radius: 0;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
.profile-section {
|
||||
background: themed("color-section");
|
||||
|
||||
.profile-section-header {
|
||||
background: themed("color-section-lighten-5");
|
||||
@include st-radius;
|
||||
padding: 0.25rem 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
content: "⮟";
|
||||
display: inline-flex;
|
||||
transition: transform 200ms;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-section-body {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0 0 radius() radius();
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Collapsed
|
||||
&.hidden {
|
||||
.profile-section-header::before {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.profile-section-body {
|
||||
padding: 0 0.5rem;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
.profile-staff-info {
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr;
|
||||
gap: 0.25rem 1rem;
|
||||
|
||||
h4 {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.block { grid-column: 1 / -1; }
|
||||
h4.block { margin-bottom: -0.25rem; }
|
||||
|
||||
@include window-larger-than(50rem) {
|
||||
grid-template-columns: min-content 1fr min-content 1fr;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
.profile-user-info {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: repeat(auto-fit, minmax(min-content, 5rem));
|
||||
background: themed("color-section");
|
||||
@include st-radius;
|
||||
|
||||
@include window-larger-than(38rem) { grid-template-columns: 1fr 1fr; }
|
||||
@include window-larger-than(50rem) { grid-template-columns: 1fr; }
|
||||
|
||||
.profile-line {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
padding: 1rem 0.5rem;
|
||||
border-bottom: 2px solid themed("color-foreground");
|
||||
|
||||
h4 {
|
||||
// Not necessary, but it aligns
|
||||
// with the -extra line better
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25em;
|
||||
|
||||
svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&-number {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-extra {
|
||||
grid-column: 1 / -1;
|
||||
font-size: 90%;
|
||||
color: themed("color-text-muted");
|
||||
|
||||
margin-left: 1.25em;
|
||||
}
|
||||
|
||||
// Stats display section
|
||||
&-show {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem 2rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
.entry {
|
||||
text-align: center;
|
||||
width: 5rem;
|
||||
& > span { color: themed("color-text-muted"); }
|
||||
}
|
||||
}
|
||||
|
||||
// Dot-separated, less important
|
||||
&-list {
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
|
||||
a:not(:last-child)::after {
|
||||
content: "•";
|
||||
margin-left: 0.25rem;
|
||||
color: themed("color-text-muted");
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
}
|
||||
}
|
@ -574,6 +574,11 @@ class User < ApplicationRecord
|
||||
base_upload_limit + (pieces[:approved] / 10) - (pieces[:deleted] / 4) - pieces[:pending]
|
||||
end
|
||||
|
||||
def upload_limit_max
|
||||
pieces = upload_limit_pieces
|
||||
base_upload_limit + (pieces[:approved] / 10) - (pieces[:deleted] / 4)
|
||||
end
|
||||
|
||||
def upload_limit_pieces
|
||||
@upload_limit_pieces ||= begin
|
||||
deleted_count = Post.deleted.for_user(id).count
|
||||
@ -739,6 +744,28 @@ class User < ApplicationRecord
|
||||
user_status.ticket_count
|
||||
end
|
||||
|
||||
def feedback_pieces
|
||||
@feedback_pieces ||= begin
|
||||
count = {
|
||||
deleted: 0,
|
||||
negative: 0,
|
||||
neutral: 0,
|
||||
positive: 0,
|
||||
}
|
||||
|
||||
feedback.each do |one|
|
||||
if one.is_deleted
|
||||
count[:deleted] += 1
|
||||
next
|
||||
end
|
||||
|
||||
count[one.category.to_sym] += 1
|
||||
end
|
||||
|
||||
count
|
||||
end
|
||||
end
|
||||
|
||||
def positive_feedback_count
|
||||
feedback.active.positive.count
|
||||
end
|
||||
|
@ -53,6 +53,11 @@ class UserPresenter
|
||||
= <abbr title="User Upload Limit Remaining">#{user.upload_limit}</abbr>}.html_safe
|
||||
end
|
||||
|
||||
def upload_limit_short
|
||||
return "none" if user.can_upload_free?
|
||||
"#{user.upload_limit} / #{user.upload_limit_max}"
|
||||
end
|
||||
|
||||
def uploads
|
||||
posts = Post.tag_match("user:#{user.name}").limit(8)
|
||||
PostsDecorator.decorate_collection(posts)
|
||||
|
34
app/views/application/_feedback_badge.erb
Normal file
34
app/views/application/_feedback_badge.erb
Normal file
@ -0,0 +1,34 @@
|
||||
<a
|
||||
href="<%= user_feedbacks_path(search: { user_id: user.id }) %>"
|
||||
class="user-records-list"
|
||||
data-negative="<%= negative %>"
|
||||
data-neutral="<%= negative %>"
|
||||
data-positive="<%= neutral %>"
|
||||
data-total="<%= positive - negative %>"
|
||||
>
|
||||
<% if deleted > 0 %>
|
||||
<span class="user-record deleted"><%= deleted %></span>
|
||||
<% end %>
|
||||
|
||||
<% if negative > 0 %>
|
||||
<span class="user-record negative"><%= negative %></span>
|
||||
<% end %>
|
||||
|
||||
<% if neutral > 0 %>
|
||||
<span class="user-record neutral"><%= neutral %></span>
|
||||
<% end %>
|
||||
|
||||
<% if positive > 0 %>
|
||||
<span class="user-record positive"><%= positive %></span>
|
||||
<% end %>
|
||||
</a>
|
||||
|
||||
<% if CurrentUser.is_moderator? && CurrentUser.user != user && active == 0 %>
|
||||
<a
|
||||
href="<%= new_user_feedback_path(user_feedback: { user_id: user.id, category: "neutral" }) %>"
|
||||
class="user-records-list"
|
||||
title="New Feedback"
|
||||
>
|
||||
<span class="user-record neutral"><%= svg_icon(:plus) %></span>
|
||||
</a>
|
||||
<% end %>
|
11
app/views/application/_profile_avatar.erb
Normal file
11
app/views/application/_profile_avatar.erb
Normal file
@ -0,0 +1,11 @@
|
||||
<a
|
||||
href="<%= user_path(user) %>"
|
||||
class="profile-avatar placeholder<%= klass.nil? ? "" : klass%>"
|
||||
data-id="<%= post_id %>"
|
||||
data-name="<%= user.name %>"
|
||||
>
|
||||
<span
|
||||
class="avatar-image"
|
||||
data-initial="<%= user.name[0].capitalize%>"
|
||||
></span>
|
||||
</a>
|
@ -20,11 +20,11 @@
|
||||
</a>
|
||||
<% if CurrentUser.is_anonymous? %>
|
||||
<a href="<%= new_session_path %>" class="simple-avatar nav-controls-profile collapse-2">
|
||||
<span class="simple-avatar-button sign-in">
|
||||
<span class="simple-avatar-name">
|
||||
<span class="avatar-button sign-in">
|
||||
<span class="avatar-name">
|
||||
Sign In
|
||||
</span>
|
||||
<span class="simple-avatar-image">
|
||||
<span class="avatar-image">
|
||||
<%= svg_icon(:log_in) %>
|
||||
</span>
|
||||
</span>
|
||||
|
@ -1,15 +1,12 @@
|
||||
<% if CurrentUser.can_view_staff_notes? %>
|
||||
<div class="staff-notes-section styled-dtext">
|
||||
<details>
|
||||
<summary>Staff Notes (<%= user.staff_notes.count %>)</summary>
|
||||
<div>
|
||||
<h4><%= link_to "Staff Notes", staff_notes_path(search: { user_id: user.id }) %></h4>
|
||||
<div class="profile-section hidden" name="StaffNotes">
|
||||
<div class="profile-section-header">Staff Notes (<%= user.staff_notes.count %>)</div>
|
||||
<div class="profile-section-body styled-dtext">
|
||||
<%= render "staff_notes/partials/list_of_notes", staff_notes: user.staff_notes.limit(15), show_receiver_name: false %>
|
||||
<div class="new-staff-note">
|
||||
<p><%= link_to "Create »", new_staff_note_path(search: { user_id: user.id }), class: "expand-new-staff-note" %></p>
|
||||
<%= render "staff_notes/partials/new", user: user, staff_note: StaffNote.new(user_id: user.id), hidden: true %>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<% end %>
|
||||
|
@ -1,14 +0,0 @@
|
||||
<div class="about-section">
|
||||
<% if user.profile_about.present? %>
|
||||
<div class="profile-about-entry" id="about-info">
|
||||
<h3>About</h3>
|
||||
<div class="content dtext-container"><%= format_text(user.profile_about, allow_color: true) %></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if user.profile_artinfo.present? %>
|
||||
<div class="profile-about-entry" id="about-artinfo">
|
||||
<h3>Artist Information</h3>
|
||||
<div class="content dtext-container"><%= format_text(user.profile_artinfo, allow_color: true) %></div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
@ -1,156 +0,0 @@
|
||||
<div class="stats-section">
|
||||
<div class="profile-avatar">
|
||||
<%= user_avatar @user %>
|
||||
</div>
|
||||
<div class="profile-stats">
|
||||
<h1><%= link_to_user @user %></h1>
|
||||
<div class="user-statistics">
|
||||
<div class="column">
|
||||
<span>Join Date</span>
|
||||
<span><%= compact_time @user.created_at %></span>
|
||||
|
||||
<span>Level</span>
|
||||
<span><%= "(Unactivated)" unless user.is_verified? %> <%= presenter.level %></span>
|
||||
|
||||
<% if user.is_banned? && user.recent_ban %>
|
||||
<span>Ban reason</span>
|
||||
<span class="dtext-container"><%= format_text presenter.ban_reason %></span>
|
||||
<% end %>
|
||||
|
||||
<span>Posts</span>
|
||||
<span>
|
||||
<%= presenter.active_upload_count(self) %>
|
||||
[<%= link_to "pending", posts_path(tags: "user:#{user.name} status:pending") %>]
|
||||
(<%= link_to "comments on", comments_path(group_by: :comment, search: {poster_id: user.id}) %>)
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
(<%= link_to "votes", action: "index", controller: "post_votes", search: { user_name: user.name } %>)
|
||||
<% end %>
|
||||
</span>
|
||||
|
||||
<span>Deleted</span>
|
||||
<span>
|
||||
<%= presenter.deleted_upload_count(self) %>
|
||||
</span>
|
||||
|
||||
<span>Replaced</span>
|
||||
<span>
|
||||
<%= presenter.replaced_upload_count(self) %>
|
||||
[<%= link_to "pending", post_replacements_path(search: { creator_name: user.name }) %>]
|
||||
</span>
|
||||
|
||||
<span>Rejected</span>
|
||||
<span><%= presenter.rejected_replacements_count(self) %></span>
|
||||
|
||||
<span>Favorites</span>
|
||||
<span>
|
||||
<%= presenter.favorite_count(self) %>
|
||||
</span>
|
||||
|
||||
<span>Forum Posts</span>
|
||||
<span>
|
||||
<%= presenter.forum_post_count(self) %>
|
||||
(<%= link_to "mentions", forum_posts_path(search: { body_matches: user.name }) %>)
|
||||
</span>
|
||||
|
||||
<span>Comments</span>
|
||||
<span>
|
||||
<%= presenter.comment_count(self) %> on <%= presenter.commented_posts_count(self) %> posts
|
||||
(<%= link_to "mentions", comments_path(group_by: :comment, search:{ body_matches: user.name }) %>)
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
(<%= link_to "votes", action: "index", controller: "comment_votes", search: { user_name: user.name } %>)
|
||||
<% end %>
|
||||
</span>
|
||||
|
||||
<% if user.can_approve_posts? || Post.where(approver: user).exists? %>
|
||||
<span>Approvals</span>
|
||||
<span><%= presenter.approval_count(self) %></span>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.user.id == user.id || CurrentUser.is_janitor? %>
|
||||
<% if presenter.previous_names(self).present? %>
|
||||
<span>Previous Names</span>
|
||||
<span><%= presenter.previous_names(self) %> -> <%= user.name %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.is_admin? %>
|
||||
<span>Email</span>
|
||||
<span>
|
||||
<%= user.email %>
|
||||
<%= email_domain_search(user.email) %>
|
||||
</span>
|
||||
<span>Last IP</span>
|
||||
<span><%= link_to_ip(user.last_ip_addr) %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<span>Feedback</span>
|
||||
<span>
|
||||
<%= presenter.feedbacks %>
|
||||
<%= link_to("List", user_feedbacks_path(search: { user_id: @user.id })) %>
|
||||
<% if CurrentUser.is_moderator? && @user.feedback.active.count == 0 %>
|
||||
| <%= link_to("Create", new_user_feedback_path(user_feedback: { user_id: @user.id, category: "neutral" })) %>
|
||||
<% end %>
|
||||
</span>
|
||||
|
||||
<span>Permissions</span>
|
||||
<span><%= presenter.permissions %></span>
|
||||
|
||||
<span>Upload Limit</span>
|
||||
<span>
|
||||
<%= presenter.upload_limit(self) %>
|
||||
<% if CurrentUser.user.id == user.id %>
|
||||
(<%= link_to "help", upload_limit_users_path %>)
|
||||
<% else %>
|
||||
(<%= link_to "help", wiki_page_path(id: "upload_limit") %>)
|
||||
<% end %>
|
||||
</span>
|
||||
|
||||
<span>Post Changes</span>
|
||||
<span>
|
||||
<%= presenter.post_version_count(self) %>
|
||||
<% if CurrentUser.is_moderator? && UserRevert.can_revert?(user) %>
|
||||
[<%= link_to "revert all", new_user_revert_path(user_id: user.id) %>]
|
||||
<% end %>
|
||||
</span>
|
||||
|
||||
<span>Wiki Changes</span>
|
||||
<span><%= presenter.wiki_page_version_count(self) %></span>
|
||||
|
||||
<span>Note Changes</span>
|
||||
<span><%= presenter.note_version_count(self) %> on <%= presenter.noted_posts_count(self) %> posts</span>
|
||||
|
||||
<span>Artist Changes</span>
|
||||
<span><%= presenter.artist_version_count(self) %></span>
|
||||
|
||||
<span>Pool Changes</span>
|
||||
<span><%= presenter.pool_version_count(self) %></span>
|
||||
|
||||
<% if CurrentUser.user.id == user.id || CurrentUser.is_janitor? %>
|
||||
<span>Flags</span>
|
||||
<span><%= presenter.flag_count(self) %></span>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.user.id == user.id || CurrentUser.is_moderator? %>
|
||||
<span>Tickets</span>
|
||||
<span>
|
||||
<%= presenter.ticket_count(self) %>
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
[<%= link_to "pending", tickets_path(search: { creator_id: user.id, status: "pending" }) %>]
|
||||
[<%= link_to "accused", tickets_path(search: { accused_id: user.id }) %>]
|
||||
<% end %>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.id == user.id %>
|
||||
<span>API Key</span>
|
||||
<span>
|
||||
<%= link_to (CurrentUser.api_key ? "View" : "Generate"), user_api_key_path(CurrentUser.user) %>
|
||||
(<%= link_to "help", help_page_path(id: "api") %>)
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
17
app/views/users/partials/show/_about.html.erb
Normal file
17
app/views/users/partials/show/_about.html.erb
Normal file
@ -0,0 +1,17 @@
|
||||
<% if has_about %>
|
||||
<tab-entry tab="about" class="profile-about-section flex">
|
||||
<tab-head>About</tab-head>
|
||||
<tab-body class="content dtext-container">
|
||||
<%= format_text(user.profile_about, allow_color: true) %>
|
||||
</tab-body>
|
||||
</tab-entry>
|
||||
<% end %>
|
||||
|
||||
<% if has_artinfo %>
|
||||
<tab-entry tab="artinfo" class="profile-artinfo-section flex">
|
||||
<tab-head>Artist Information</tab-head>
|
||||
<tab-body class="content dtext-container">
|
||||
<%= format_text(user.profile_artinfo, allow_color: true) %>
|
||||
</tab-body>
|
||||
</tab-entry>
|
||||
<% end %>
|
5
app/views/users/partials/show/_ban_banner.html.erb
Normal file
5
app/views/users/partials/show/_ban_banner.html.erb
Normal file
@ -0,0 +1,5 @@
|
||||
<% if user.is_banned? && user.recent_ban %>
|
||||
<div class="profile-ban dtext-container">
|
||||
<%= format_text presenter.ban_reason %>
|
||||
</div>
|
||||
<% end %>
|
20
app/views/users/partials/show/_card.html.erb
Normal file
20
app/views/users/partials/show/_card.html.erb
Normal file
@ -0,0 +1,20 @@
|
||||
<div class="profile-card">
|
||||
<div class="profile-avatar">
|
||||
<%= profile_avatar(@user) %>
|
||||
</div>
|
||||
<div class="profile-info">
|
||||
<span class="profile-name">
|
||||
<%= link_to_user(@user) %>
|
||||
<%= user_feedback_badge(@user) %>
|
||||
</span>
|
||||
<span class="profile-joined">
|
||||
Joined <%= compact_date @user.created_at %>
|
||||
</span>
|
||||
<span class="profile-rank">
|
||||
<%= user_level_badge(@user) %>
|
||||
<% unless @user.is_verified? %>
|
||||
<span class="level-badge level-unactivated">UNACTIVATED</span>
|
||||
<% end %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
0
app/views/users/partials/show/_mentions.html.erb
Normal file
0
app/views/users/partials/show/_mentions.html.erb
Normal file
@ -4,7 +4,7 @@
|
||||
<div class="profile-sample profile-uploads">
|
||||
|
||||
<div class="profile-sample-header">
|
||||
<%= link_to "Uploads", posts_path(:tags => "user:#{user.name}"), class: "title" %>
|
||||
<%= link_to "Uploads", posts_path(tags: "user:#{user.name}"), class: "title" %>
|
||||
</div>
|
||||
|
||||
<div class="profile-sample-links">
|
||||
@ -29,11 +29,11 @@
|
||||
<div class="profile-sample profile-favorites">
|
||||
|
||||
<div class="profile-sample-header">
|
||||
<%= link_to "Favorites", favorites_path(:user_id => user.id), class: "title" %>
|
||||
<%= link_to "Favorites", favorites_path(user_id: user.id), class: "title" %>
|
||||
</div>
|
||||
|
||||
<div class="profile-sample-links">
|
||||
<%= link_to(sanitize("<b>#{user.favorite_count}</b> total"), favorites_path(:user_id => user.id)) %>
|
||||
<%= link_to(sanitize("<b>#{user.favorite_count}</b> total"), favorites_path(user_id: user.id)) %>
|
||||
<% if user.enable_privacy_mode? || user.is_blocked? %>
|
||||
<span>[hidden]</span>
|
||||
<% end %>
|
34
app/views/users/partials/show/_staff_info.html.erb
Normal file
34
app/views/users/partials/show/_staff_info.html.erb
Normal file
@ -0,0 +1,34 @@
|
||||
<div class="profile-section hidden" name="StaffStats">
|
||||
<div class="profile-section-header">Staff Info</div>
|
||||
<div class="profile-section-body profile-staff-info">
|
||||
<% if presenter.previous_names(self).present? %>
|
||||
<h4 class="block">Previous Names</h4>
|
||||
<span class="block"><%= presenter.previous_names(self) %> -> <%= user.name %></span>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.is_admin? %>
|
||||
<h4>Email</h4>
|
||||
<span>
|
||||
<%= user.email %>
|
||||
<%= email_domain_search(user.email) %>
|
||||
</span>
|
||||
|
||||
<h4>Last IP</h4>
|
||||
<span><%= link_to_ip(user.last_ip_addr) %></span>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
<h4>Votes</h4>
|
||||
<span>
|
||||
<%= link_to "Posts", action: "index", controller: "post_votes", search: { user_name: user.name } %>
|
||||
| <%= link_to "Comments", action: "index", controller: "comment_votes", search: { user_name: user.name } %>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<h4>Pending</h4>
|
||||
<span>
|
||||
<%= link_to "Posts", posts_path(tags: "user:#{user.name} status:pending") %>
|
||||
| <%= link_to "Replacements", post_replacements_path(search: { creator_name: user.name }) %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
129
app/views/users/partials/show/_user_info.html.erb
Normal file
129
app/views/users/partials/show/_user_info.html.erb
Normal file
@ -0,0 +1,129 @@
|
||||
<tab-entry tab="stats" class="profile-user-info">
|
||||
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:message_square) %> Comments</h4>
|
||||
<span class="profile-line-number">
|
||||
<%= presenter.comment_count(self) %>
|
||||
</span>
|
||||
<span class="profile-line-extra">
|
||||
<%= link_to "[mentions]", comments_path(group_by: :comment, search: { body_matches: user.name }) %>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:lectern) %> Forum posts</h4>
|
||||
<span class="profile-line-number">
|
||||
<%= presenter.forum_post_count(self) %>
|
||||
</span>
|
||||
<span class="profile-line-extra">
|
||||
<%= link_to "[mentions]", forum_posts_path(search: { body_matches: user.name }) %>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<% if user.can_approve_posts? || Post.where(approver: user).exists? %>
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:stamp) %> Approvals</h4>
|
||||
<span class="profile-line-number">
|
||||
<%= presenter.approval_count(self) %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:upload) %> Upload Limit</h4>
|
||||
<span class="profile-line-number">
|
||||
<% if CurrentUser.user.id == user.id %>
|
||||
<%= link_to presenter.upload_limit_short, upload_limit_users_path %>
|
||||
<% else %>
|
||||
<%= link_to presenter.upload_limit_short, wiki_page_path(id: "upload_limit") %>
|
||||
<% end %>
|
||||
</span>
|
||||
<span class="profile-line-extra">
|
||||
Max number of unapproved posts at a time.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:replace) %> Changes</h4>
|
||||
<span class="profile-line-extra profile-line-show">
|
||||
|
||||
<% if user.post_update_count != 0 %>
|
||||
<a href="<%= post_versions_path(lr: user.id, search: { updater_id: user.id }) %>" class="entry">
|
||||
<h5><%= user.post_update_count %></h5>
|
||||
<span>Post</span>
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<% if user.pool_version_count != 0 %>
|
||||
<a href="<%= pool_versions_path(search: { updater_id: user.id }) %>" class="entry">
|
||||
<h5><%= user.pool_version_count %></h5>
|
||||
<span>Pool</span>
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<% if user.artist_version_count != 0 %>
|
||||
<a href="<%= artist_versions_path(search: { updater_id: user.id }) %>" class="entry">
|
||||
<h5><%= user.artist_version_count %></h5>
|
||||
<span>Artist</span>
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<% if user.wiki_page_version_count != 0 %>
|
||||
<a href="<%= wiki_page_versions_path(search: { updater_id: user.id }) %>" class="entry">
|
||||
<h5><%= user.wiki_page_version_count %></h5>
|
||||
<span>Wiki</span>
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<% if user.note_version_count != 0 %>
|
||||
<a href="<%= note_versions_path(search: { updater_id: user.id }) %>" class="entry">
|
||||
<h5><%= user.note_version_count %></h5>
|
||||
<span>Note</span>
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<% if CurrentUser.user.id == user.id || CurrentUser.is_approver? %>
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:flag_left) %> Flags</h4>
|
||||
<span class="profile-line-number"><%= presenter.flag_count(self) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.user.id == user.id || CurrentUser.is_moderator? %>
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:ticket) %> Tickets</h4>
|
||||
<span class="profile-line-number"><%= presenter.ticket_count(self) %></span>
|
||||
<% if CurrentUser.is_moderator? %>
|
||||
<span class="profile-line-extra">
|
||||
[<%= link_to "pending", tickets_path(search: { creator_id: user.id, status: "pending" }) %>]
|
||||
[<%= link_to "accused", tickets_path(search: { accused_id: user.id }) %>]
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if CurrentUser.user.id == user.id %>
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:key_square) %> API Key</h4>
|
||||
<span class="profile-line-number">
|
||||
<%= link_to (CurrentUser.api_key ? "View" : "Generate"), user_api_key_path(CurrentUser.user) %>
|
||||
</span>
|
||||
<span class="profile-line-extra">
|
||||
(<%= link_to "help", help_page_path(id: "api") %>)
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% permissions = presenter.permissions %>
|
||||
<% unless permissions.empty? %>
|
||||
<div class="profile-line">
|
||||
<h4><%= svg_icon(:power) %> Permissions</h4>
|
||||
<span class="profile-line-extra">
|
||||
<%= permissions %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</tab-entry>
|
@ -1,10 +1,41 @@
|
||||
<div id="c-users">
|
||||
<div id="a-show">
|
||||
<%= render "statistics", :presenter => @presenter, :user => @user %>
|
||||
|
||||
<!-- Header -->
|
||||
<%= render "/users/partials/show/card", :presenter => @presenter, :user => @user %>
|
||||
<%= render "/users/partials/show/ban_banner", :presenter => @presenter, :user => @user %>
|
||||
|
||||
<% if CurrentUser.is_staff? %>
|
||||
<!-- Staff Info -->
|
||||
<%= render "/users/partials/show/staff_info", :presenter => @presenter, :user => @user %>
|
||||
<%= render "staff_notes/partials/for_user", user: @user %>
|
||||
<% end %>
|
||||
|
||||
<!-- Blacklist -->
|
||||
<%= render "posts/partials/common/inline_blacklist" %>
|
||||
<%= render "post_summary", presenter: @presenter, user: @user %>
|
||||
<%= render "about", presenter: @presenter, user: @user %>
|
||||
|
||||
<!-- Central panel -->
|
||||
<% has_about = @user.profile_about.present? %>
|
||||
<% has_artinfo = @user.profile_artinfo.present? %>
|
||||
<tabs-menu
|
||||
id="profile-tabs"
|
||||
data-has-about="<%= has_about %>"
|
||||
data-has-artinfo="<%= has_artinfo %>"
|
||||
>
|
||||
<% if has_about %>
|
||||
<button role="tab" name="about">About</button>
|
||||
<% end %>
|
||||
<% if has_artinfo %>
|
||||
<button role="tab" name="artinfo">Commission Info</button>
|
||||
<% end %>
|
||||
<button role="tab" name="stats">Stats</button>
|
||||
</tabs-menu>
|
||||
|
||||
<tabs-content for="profile-tabs">
|
||||
<%= render "/users/partials/show/user_info", :presenter => @presenter, :user => @user %>
|
||||
<%= render "/users/partials/show/about", presenter: @presenter, user: @user, has_about: has_about, has_artinfo: has_artinfo %>
|
||||
<%= render "/users/partials/show/post_summary", presenter: @presenter, user: @user %>
|
||||
</tabs-content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user