better styling for fa posts, users, index pages
This commit is contained in:
@@ -4,5 +4,9 @@
|
||||
"trailingComma": "all",
|
||||
"arrowParens": "always",
|
||||
"singleQuote": true,
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
"plugins": [
|
||||
"prettier-plugin-tailwindcss",
|
||||
"@prettier/plugin-ruby",
|
||||
"@4az/prettier-plugin-html-erb"
|
||||
]
|
||||
}
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -9,7 +9,7 @@
|
||||
"editor.defaultFormatter": "Shopify.ruby-lsp"
|
||||
},
|
||||
"[erb]": {
|
||||
"editor.defaultFormatter": "aliariff.vscode-erb-beautify"
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
@@ -45,5 +45,6 @@
|
||||
},
|
||||
"tailwindCSS.experimental.configFile": "config/tailwind.config.js",
|
||||
"sqliteViewer.maxFileSize": 4000,
|
||||
"files.insertFinalNewline": true
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
|
||||
6
Gemfile
6
Gemfile
@@ -134,6 +134,10 @@ gem "react_on_rails"
|
||||
gem "sanitize"
|
||||
gem "shakapacker"
|
||||
|
||||
gem "syntax_tree", "~> 6.2"
|
||||
group :development do
|
||||
gem "prettier_print"
|
||||
gem "syntax_tree", "~> 6.2"
|
||||
end
|
||||
|
||||
gem "cssbundling-rails", "~> 1.4"
|
||||
gem "tailwindcss-rails", "~> 3.0"
|
||||
|
||||
@@ -442,6 +442,7 @@ DEPENDENCIES
|
||||
pg_query (>= 2)
|
||||
pghero!
|
||||
pluck_each
|
||||
prettier_print
|
||||
progressbar
|
||||
pry
|
||||
pry-stack_explorer
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.animated-shadow-sky {
|
||||
@apply shadow-lg;
|
||||
}
|
||||
|
||||
.sky-section {
|
||||
@apply overflow-hidden rounded-lg border border-slate-300 bg-slate-100 divide-y divide-slate-300;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
@apply px-4 py-3 font-medium text-slate-900;
|
||||
}
|
||||
|
||||
.sky-link {
|
||||
@apply text-sky-600 underline decoration-dotted hover:text-sky-800 transition-colors;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ module Domain::Fa::PostsHelper
|
||||
|
||||
def page_str(params)
|
||||
if (params[:page] || 1).to_i > 1
|
||||
"(Page #{params[:page]})"
|
||||
"(page #{params[:page]})"
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ import Icon from './Icon';
|
||||
const COMMON_LIST_ELEM_CLASSES = `
|
||||
w-full p-2
|
||||
text-xl font-light
|
||||
border-slate-200 border-2
|
||||
border-inherit
|
||||
group-focus-within:border-slate-300
|
||||
`;
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ const CONFIG = {
|
||||
|
||||
const STYLES = {
|
||||
LIST_ELEM_CLASSNAME: [
|
||||
'w-full p-2 pl-8 text-xl font-light border-slate-200 border-2',
|
||||
'group-focus-within:border-slate-300',
|
||||
'w-full p-2 pl-8 text-xl font-light border-slate-300 border-2',
|
||||
'group-focus-within:border-slate-400',
|
||||
],
|
||||
SVG_BASE_CLASSNAME: `stroke-slate-500 fill-slate-500`,
|
||||
SVG_FOCUSABLE_CLASSNAME: `stroke-slate-500 fill-slate-500 group-focus-within:stroke-slate-800 group-focus-within:fill-slate-800`,
|
||||
@@ -183,7 +183,9 @@ export default function UserSearchBar({ isServerRendered }: PropTypes) {
|
||||
|
||||
function UserSearchBarItems() {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={`${anyShown || 'border-b-0'} divide-y divide-inherit rounded-b-lg border border-t-0 border-inherit`}
|
||||
>
|
||||
{visibility.error ? (
|
||||
<ListItem
|
||||
key="error"
|
||||
@@ -260,9 +262,9 @@ export default function UserSearchBar({ isServerRendered }: PropTypes) {
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'group mx-auto w-full rounded-xl p-2 transition-all duration-1000',
|
||||
'focus-within:border-slate-200 focus-within:shadow-md sm:max-w-md',
|
||||
'sm:border-2 sm:border-slate-100 sm:p-2',
|
||||
'group mx-auto w-full p-2 transition-colors duration-1000 sm:rounded-xl',
|
||||
'focus-within:border-slate-400 sm:max-w-md',
|
||||
'border-slate-300 bg-slate-50 p-2 shadow-lg',
|
||||
].join(' ')}
|
||||
>
|
||||
<label className={`relative block ${STYLES.INPUT_CLASSNAME}`}>
|
||||
@@ -282,7 +284,7 @@ export default function UserSearchBar({ isServerRendered }: PropTypes) {
|
||||
STYLES.LIST_ELEM_CLASSNAME,
|
||||
STYLES.INPUT_CLASSNAME,
|
||||
'rounded-lg outline-none',
|
||||
'bg-slate-100 placeholder:italic',
|
||||
'bg-slate-50 placeholder:italic',
|
||||
anyShown && 'rounded-b-none',
|
||||
]
|
||||
.filter(Boolean)
|
||||
|
||||
@@ -52,6 +52,10 @@ class Domain::Fa::Post < ReduxApplicationRecord
|
||||
foreign_key: :post_id,
|
||||
dependent: :destroy
|
||||
|
||||
def to_param
|
||||
self.fa_id.to_s
|
||||
end
|
||||
|
||||
def file_uri
|
||||
Addressable::URI.parse(self.file_url_str) if self.file_url_str
|
||||
end
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
<div id="<%= dom_id post %>" class="w-full max-w-2xl mt-4 mx-auto p-2">
|
||||
<section class='border-2 border-slate-300 rounded-md p-1 mb-2'>
|
||||
<span class='text-md italic'><%= @post.title %></span> -
|
||||
<div
|
||||
id="<%= dom_id post %>"
|
||||
class="mx-auto mt-4 flex w-full max-w-2xl flex-col gap-4 pb-4"
|
||||
>
|
||||
<section class="rounded-md border border-slate-300 bg-slate-50 p-4">
|
||||
<span class="text-md italic"><%= @post.title %></span> -
|
||||
<%= render "domain/fa/users/inline_link", user: @post.creator %>
|
||||
<div><%= scanned_and_file_description(@post) %></div>
|
||||
</section>
|
||||
<section>
|
||||
<% if @post.file %>
|
||||
<%= render partial: "log_entries/content_container", locals: { log_entry: @post.file } %>
|
||||
<%= render partial: "log_entries/content_container",
|
||||
locals: {
|
||||
log_entry: @post.file,
|
||||
} %>
|
||||
<% else %>
|
||||
<% if !@post.scanned? %>
|
||||
<%= button_to "Force scan post", scan_post_domain_fa_post_path(fa_id: @post.fa_id) %>
|
||||
@@ -17,41 +23,51 @@
|
||||
<% end %>
|
||||
<% end %>
|
||||
</section>
|
||||
<section class='border-2 border-slate-300 rounded-md mb-2 overflow-clip'>
|
||||
<section class="sky-section">
|
||||
<% if (post_description_html = @post.description) %>
|
||||
<div class='text-lg border-b-2 border-b-slate-300 text-slate-600 p-1 italic'>Post Description</div>
|
||||
<div class='bg-slate-800 text-slate-200 p-2'>
|
||||
<div class="section-header">Post Description</div>
|
||||
<div class="bg-slate-800 p-4 text-slate-200">
|
||||
<%= fa_post_description_sanitized(post_description_html) %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div>(No post description)</div>
|
||||
<% end %>
|
||||
</section>
|
||||
<section class='border-2 border-slate-300 rounded-md'>
|
||||
<div class='text-lg border-b-2 border-b-slate-300 text-slate-600 p-1 italic'>Similar Posts</div>
|
||||
<div class='bg-slate-100'>
|
||||
<section class="sky-section">
|
||||
<div class="section-header">Similar Posts</div>
|
||||
<div class="bg-slate-100">
|
||||
<% cache(@post.disco, expires_in: 12.hours) do %>
|
||||
<% similar = @post.
|
||||
disco&.
|
||||
nearest_neighbors(:for_favorite, distance: "euclidean")&.
|
||||
limit(10)&.
|
||||
includes(:post)
|
||||
%>
|
||||
<% similar =
|
||||
@post
|
||||
.disco
|
||||
&.nearest_neighbors(:for_favorite, distance: "euclidean")
|
||||
&.limit(10)
|
||||
&.includes(:post) %>
|
||||
<% if similar %>
|
||||
<% similar.each do |factor| %>
|
||||
<% post = factor.post %>
|
||||
<div class='flex flex-row justify-between items-center py-1 px-2 border-b-2 last:border-b-0'>
|
||||
<div class='flex flex-row items-center'>
|
||||
<span class='text-md italic'><%= link_to post.title, domain_fa_post_path(post.fa_id), class: 'underline' %></span>
|
||||
<span class='italic ml-2'>by <%= render "domain/fa/users/inline_link", user: post.creator %></span>
|
||||
<div
|
||||
class="flex flex-row items-center justify-between border-b px-2 py-1 last:border-b-0"
|
||||
>
|
||||
<div class="flex flex-row items-center">
|
||||
<span class="text-md italic"
|
||||
><%= link_to post.title, domain_fa_post_path(post.fa_id), class: "underline" %></span
|
||||
>
|
||||
<span class="ml-2 italic"
|
||||
>by
|
||||
<%= render "domain/fa/users/inline_link", user: post.creator %></span
|
||||
>
|
||||
</div>
|
||||
<span class='text-sm text-slate-500 ml-2'>
|
||||
(distance: <%= number_with_precision(factor.neighbor_distance, precision: 5) %>)
|
||||
<span class="ml-2 text-sm text-slate-500">
|
||||
(distance:
|
||||
<%= number_with_precision(factor.neighbor_distance, precision: 5) %>)
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class='p-2'>No similar posts</div>
|
||||
<div class="p-4 text-center italic text-slate-400">
|
||||
No similar posts
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
@@ -1,42 +1,70 @@
|
||||
<div class='mx-auto mt-4 sm:mt-6 text-center'>
|
||||
<div class="mx-auto mt-4 text-center sm:mt-6">
|
||||
<% if @user %>
|
||||
<h1 class='text-2xl'><%= link_to(@user.name, @user, class: "underline") %>'s posts (page <%= page_str(params) || 1 %>)</h1>
|
||||
<h1 class="flex text-2xl">
|
||||
<%= link_to(@user, class: "sky-link flex items-center gap-2") do %>
|
||||
<img
|
||||
src="<%= fa_user_avatar_path(@user, thumb: "64-avatar") %>"
|
||||
class="inline-block h-8 w-8 rounded-md"
|
||||
/>
|
||||
<span><%= @user.name %></span>
|
||||
<% end %>
|
||||
<span>'s posts <%= page_str(params) %></span>
|
||||
</h1>
|
||||
<% else %>
|
||||
<h1 class='text-2xl'>All FurAffinity posts, page <%= page_str(params) || 1 %></h1>
|
||||
<h1 class="text-2xl">
|
||||
All FurAffinity posts, page <%= page_str(params) || 1 %>
|
||||
</h1>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render partial: "shared/pagination_controls", locals: { collection: @posts } %>
|
||||
<div class='flex flex-row flex-wrap'>
|
||||
<div class="mx-auto flex flex-row flex-wrap justify-center">
|
||||
<% @posts.each do |post| %>
|
||||
<div class='border border-gray-300 rounded flex-shrink-0 bg-slate-50 m-4 flex flex-col'>
|
||||
<div class='p-4 flex items-center justify-center flex-1'>
|
||||
<div
|
||||
class="m-4 flex flex-shrink-0 flex-col rounded-lg border border-slate-300 bg-slate-50 shadow-sm"
|
||||
>
|
||||
<div class="flex flex-1 items-center justify-center p-4">
|
||||
<% if post.file %>
|
||||
<%= link_to domain_fa_post_path(post.fa_id) do %>
|
||||
<img class='max-w-[300px] max-h-[300px] object-contain border border-gray-300 shadow-md' alt='<%= post.title %>' src='<%= contents_blob_path(HexUtil.bin2hex(post.file.response_sha256), format: "jpg", thumb: "small") %>' />
|
||||
<img
|
||||
class="max-h-[300px] max-w-[300px] rounded-md border border-slate-300 object-contain shadow-md"
|
||||
alt="<%= post.title %>"
|
||||
src="<%= contents_blob_path(
|
||||
HexUtil.bin2hex(post.file.response_sha256),
|
||||
format: "jpg",
|
||||
thumb: "small",
|
||||
) %>"
|
||||
/>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<span>No file for <%= post.fa_id %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class='border-t border-gray-300'>
|
||||
<h2 class='text-lg p-4 text-center'>
|
||||
<%= link_to post.title, domain_fa_post_path(post.fa_id), class: "hover:underline" %>
|
||||
<div class="border-t border-slate-300">
|
||||
<h2 class="p-4 text-center text-lg">
|
||||
<%= link_to post.title, domain_fa_post_path(post.fa_id), class: "sky-link" %>
|
||||
</h2>
|
||||
<div class='px-4 pb-4 text-sm text-gray-600'>
|
||||
<div class='flex justify-between items-start'>
|
||||
<div class="px-4 pb-4 text-sm text-slate-600">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<% if post.posted_at %>
|
||||
Posted <%= post.posted_at.strftime("%Y-%m-%d") %>
|
||||
<div class="text-gray-500">
|
||||
<div class="text-slate-500">
|
||||
(<%= time_ago_in_words(post.posted_at) %> ago)
|
||||
</div>
|
||||
<% else %>
|
||||
Posted date unknown
|
||||
<% end %>
|
||||
</div>
|
||||
<%= link_to "https://www.furaffinity.net/view/#{post.fa_id}", target: "_blank", rel: "noopener", class: "inline-flex items-center text-gray-500 hover:text-gray-700 ml-8" do %>
|
||||
<%= link_to "https://www.furaffinity.net/view/#{post.fa_id}",
|
||||
target: "_blank",
|
||||
rel: "noopener",
|
||||
class:
|
||||
"inline-flex items-center text-slate-500 hover:text-slate-700 ml-8 decoration-dotted underline" do %>
|
||||
<span>FA #<%= post.fa_id %></span>
|
||||
<%= render partial: "shared/icons/external_link", locals: { class_name: "w-4 h-4 ml-1" } %>
|
||||
<%= render partial: "shared/icons/external_link",
|
||||
locals: {
|
||||
class_name: "w-4 h-4 ml-1",
|
||||
} %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
<div class="flex items-center w-full">
|
||||
<a href="<%= domain_fa_user_path(user) %>" class="grow flex items-center gap-2">
|
||||
<img src="<%= fa_user_avatar_path(user, thumb: "64-avatar") %>" class="w-8 h-8"/>
|
||||
<span class='underline decoration-dashed text-slate-600 hover:text-slate-900'><%= user.url_name %></span>
|
||||
<div class="flex w-full items-center">
|
||||
<a
|
||||
class="flex grow items-center gap-2"
|
||||
href="<%= domain_fa_user_path(user) %>"
|
||||
>
|
||||
<img
|
||||
src="<%= fa_user_avatar_path(user, thumb: "64-avatar") %>"
|
||||
class="h-8 w-8 rounded-md"
|
||||
/>
|
||||
<span class="sky-link"> <%= user.url_name %> </span>
|
||||
</a>
|
||||
<span class="text-slate-500 text-sm"><%= pluralize(number_with_delimiter(user.posts.count, delimiter: ","), "post") %></span>
|
||||
<span class="text-slate-500"
|
||||
><%= pluralize(number_with_delimiter(user.posts.count, delimiter: ","), "post") %></span
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<section class='border border-slate-300 rounded-lg overflow-hidden bg-white shadow-sm'>
|
||||
<div class='px-4 py-3 border-b border-slate-200'>
|
||||
<h2 class='text-lg font-medium text-slate-900'>Recent Posts</h2>
|
||||
</div>
|
||||
<div>
|
||||
<% if user.posts.any? %>
|
||||
<div class="divide-y divide-slate-200">
|
||||
<% user.posts.order(created_at: :desc).limit(5).each do |post| %>
|
||||
<div class='flex items-center px-4 py-2'>
|
||||
<span class="grow text-slate-600">
|
||||
<%= link_to post.title, "https://www.furaffinity.net/view/#{post.fa_id}/",
|
||||
class: "underline decoration-slate-300 hover:decoration-slate-600 hover:text-slate-900 transition-all",
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer" %>
|
||||
</span>
|
||||
<span class="text-slate-900"><%= time_ago_in_words(post.created_at) %> ago</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class='px-4 py-3 text-slate-500'>No posts found</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
@@ -1,75 +1,13 @@
|
||||
<div id="<%= dom_id user %>" class="w-full max-w-2xl mt-6 mx-auto space-y-6">
|
||||
<section class='flex flex-row border border-slate-300 rounded-lg p-4 bg-white shadow-sm'>
|
||||
<div class="grow flex items-center gap-4">
|
||||
<img src="<%= fa_user_avatar_path(user) %>" class="w-12 h-12 rounded-lg"/>
|
||||
<div>
|
||||
<div class="text-lg font-medium text-slate-900"><%= user.url_name %></div>
|
||||
<div class='text-slate-500 text-sm'>
|
||||
<span><%= fa_user_account_status(user) %></span> •
|
||||
<span><%= user.state %></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="<%= dom_id user %>" class="mx-auto my-4 w-full max-w-2xl space-y-4">
|
||||
<%= render "domain/fa/users/show_sections/name_icon_and_status", user: user %>
|
||||
<div class="flex flex-row gap-4">
|
||||
<div class="w-1/2">
|
||||
<%= render "domain/fa/users/show_sections/stats", user: user %>
|
||||
</div>
|
||||
<a
|
||||
href="https://www.furaffinity.net/user/<%= user.url_name %>/"
|
||||
target="_blank" rel='noopener noreferrer'
|
||||
class="flex items-center gap-2 text-slate-600 hover:text-slate-900 transition-colors"
|
||||
>
|
||||
<span class="font-medium">FurAffinity</span>
|
||||
<img src="<%= image_path("fa-logo.png") %>" class='w-5 h-5' />
|
||||
</a>
|
||||
</section>
|
||||
<section class='border border-slate-300 rounded-lg bg-white shadow-sm divide-y divide-slate-200'>
|
||||
<% [
|
||||
["Posts", user.posts.count],
|
||||
["Favorites", user.num_favorites],
|
||||
["Following", user.follower_joins.count],
|
||||
["Followed by", user.followed_joins.count],
|
||||
["Gallery scanned", user.time_ago_for_gallery_scan],
|
||||
["Page scanned", user.time_ago_for_page_scan]
|
||||
].each do |label, value| %>
|
||||
<div class='flex items-center px-4 py-2'>
|
||||
<span class="grow text-slate-600"><%= label %></span>
|
||||
<span class="text-slate-900"><%= value.is_a?(Integer) ? number_with_delimiter(value, delimiter: ",") : value %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</section>
|
||||
<%= render "recent_posts_section", user: user %>
|
||||
<section class='border border-slate-300 rounded-lg overflow-hidden bg-white shadow-sm'>
|
||||
<% if (profile_html = user.profile_html) %>
|
||||
<div class='px-4 py-3 border-b border-slate-200 bg-white'>
|
||||
<h2 class='text-lg font-medium text-slate-900'>Profile Description</h2>
|
||||
</div>
|
||||
<div class='p-4 bg-slate-800 text-slate-200'>
|
||||
<% cache(user, expires_in: 12.hours) do %>
|
||||
<%= sanitized_fa_user_profile_html(profile_html) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="px-4 py-3 text-slate-500">No profile description available</div>
|
||||
<% end %>
|
||||
</section>
|
||||
<section class='border border-slate-300 rounded-lg overflow-hidden bg-white shadow-sm'>
|
||||
<div class='px-4 py-3 border-b border-slate-200'>
|
||||
<h2 class='text-lg font-medium text-slate-900'>Similar Users</h2>
|
||||
<div class="w-1/2">
|
||||
<%= render "domain/fa/users/show_sections/recent_posts", user: user %>
|
||||
</div>
|
||||
<div>
|
||||
<% cache(user.disco, expires_in: 12.hours) do %>
|
||||
<% similar = similar_users_by_followed(user)&.includes(:avatar) %>
|
||||
<% if similar %>
|
||||
<div class="divide-y divide-slate-200">
|
||||
<% similar.each do |user| %>
|
||||
<div class='flex items-center px-4 py-2'>
|
||||
<span class="grow">
|
||||
<%= render "inline_link", user: user %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class='px-4 py-3 text-slate-500'>No similar users found</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<%= render "domain/fa/users/show_sections/profile_description", user: user %>
|
||||
<%= render "domain/fa/users/show_sections/similar_users", user: user %>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<section class="animated-shadow-sky sky-section flex divide-none p-3">
|
||||
<div class="flex grow items-center gap-4">
|
||||
<img src="<%= fa_user_avatar_path(user) %>" class="h-12 w-12 rounded-lg" />
|
||||
<div>
|
||||
<div class="text-lg font-medium text-slate-900"><%= user.url_name %></div>
|
||||
<div class="text-sm text-slate-500">
|
||||
<span><%= fa_user_account_status(user) %></span> •
|
||||
<span><%= user.state %></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
href="https://www.furaffinity.net/user/<%= user.url_name %>/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="sky-link flex items-center gap-2"
|
||||
>
|
||||
<span class="font-medium">FurAffinity</span>
|
||||
<img src="<%= image_path("fa-logo.png") %>" class="h-5 w-5" />
|
||||
</a>
|
||||
</section>
|
||||
@@ -0,0 +1,12 @@
|
||||
<section class="animated-shadow-sky sky-section">
|
||||
<% if (profile_html = user.profile_html) %>
|
||||
<h2 class="section-header">Profile Description</h2>
|
||||
<div class="bg-slate-800 p-4 text-slate-200">
|
||||
<% cache(user, expires_in: 12.hours) do %>
|
||||
<%= sanitized_fa_user_profile_html(profile_html) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="px-4 py-3 text-slate-500">No profile description available</div>
|
||||
<% end %>
|
||||
</section>
|
||||
@@ -0,0 +1,28 @@
|
||||
<section class="animated-shadow-sky sky-section">
|
||||
<h2 class="section-header">
|
||||
<span class="font-medium text-slate-900">Recent Posts</span>
|
||||
<span class="float-right">
|
||||
<%= link_to "#{user.posts.count} total",
|
||||
domain_fa_user_posts_path(user),
|
||||
class: "sky-link" %>
|
||||
</span>
|
||||
</h2>
|
||||
<% if user.posts.any? %>
|
||||
<% user
|
||||
.posts
|
||||
.order(created_at: :desc)
|
||||
.limit(5)
|
||||
.each do |post| %>
|
||||
<div class="flex items-center px-4 py-2">
|
||||
<span class="grow">
|
||||
<%= link_to post.title, domain_fa_post_path(post), class: "sky-link" %>
|
||||
</span>
|
||||
<span class="text-slate-500">
|
||||
<%= time_ago_in_words(post.created_at) %> ago
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="px-4 py-3 text-slate-500">No posts found</div>
|
||||
<% end %>
|
||||
</section>
|
||||
@@ -0,0 +1,15 @@
|
||||
<section class="animated-shadow-sky sky-section">
|
||||
<h2 class="section-header">Similar Users</h2>
|
||||
<% cache(user.disco, expires_in: 12.hours) do %>
|
||||
<% similar = similar_users_by_followed(user)&.includes(:avatar) %>
|
||||
<% if similar %>
|
||||
<% similar.each do |user| %>
|
||||
<div class="flex items-center px-4 py-2">
|
||||
<%= render "inline_link", user: user %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="px-4 py-3 text-slate-500">No similar users found</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</section>
|
||||
17
app/views/domain/fa/users/show_sections/_stats.html.erb
Normal file
17
app/views/domain/fa/users/show_sections/_stats.html.erb
Normal file
@@ -0,0 +1,17 @@
|
||||
<section class="sky-section animated-shadow-sky divide-y">
|
||||
<h2 class="section-header">User Stats</h2>
|
||||
<% [
|
||||
["Favorites", user.num_favorites],
|
||||
["Following", user.follower_joins.count],
|
||||
["Followed by", user.followed_joins.count],
|
||||
["Gallery scanned", user.time_ago_for_gallery_scan],
|
||||
["Page scanned", user.time_ago_for_page_scan],
|
||||
].each do |label, value| %>
|
||||
<div class="flex items-center px-4 py-2">
|
||||
<span class="grow text-slate-900"><%= label %></span>
|
||||
<span class="text-slate-500">
|
||||
<%= value.is_a?(Integer) ? number_with_delimiter(value, delimiter: ",") : value %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</section>
|
||||
@@ -30,7 +30,7 @@
|
||||
</h2>
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex flex-col grow">
|
||||
<main class="flex flex-col grow bg-slate-200">
|
||||
<%= yield %>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
<% content_for :head do %>
|
||||
<%# <%= javascript_pack_tag "application-bundle" %>
|
||||
<% end %>
|
||||
<div class='w-full mt-2 sm:mt-6'>
|
||||
<div class="mt-2 sm:m-2 sm:p-4">
|
||||
<%= react_component("UserSearchBar", props: {}, prerender: true, strict_mode: true) %>
|
||||
<div class="mt-2 mx-auto w-full p-2 sm:p-2 sm:border-2 sm:border-slate-100 sm:max-w-md rounded-xl">
|
||||
<div class='text-slate-400 italic'>Questions? Comments? Suggestions?</div>
|
||||
<div class='text-slate-400 italic' >Contact @DeltaNoises -
|
||||
<%= link_to "Telegram", "https://t.me/DeltaNoises", target: "_blank", class: "underline decoration-dashed" %> -
|
||||
<%= link_to "BlueSky", "https://bsky.app/profile/delta.refurrer.com", target: "_blank", class: "underline decoration-dashed" %>
|
||||
<div
|
||||
class="mx-auto mt-2 w-full border-y border-slate-300 bg-slate-50 p-2 shadow-lg sm:max-w-md sm:rounded-xl sm:border"
|
||||
>
|
||||
<div class="font-light italic text-slate-500">
|
||||
Questions? Comments? Suggestions?
|
||||
</div>
|
||||
<div class="font-light italic text-slate-500">
|
||||
Contact @DeltaNoises -
|
||||
<%= link_to "Telegram",
|
||||
"https://t.me/DeltaNoises",
|
||||
target: "_blank",
|
||||
class: "sky-link" %>
|
||||
-
|
||||
<%= link_to "BlueSky",
|
||||
"https://bsky.app/profile/delta.refurrer.com",
|
||||
target: "_blank",
|
||||
class: "sky-link" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
<div class='mx-auto w-full border-2 border-slate-300 sm:max-w-md rounded-md mt-4 sm:mt-6 mb-4 sm:mb-6'>
|
||||
<div class='flex justify-center items-stretch h-full'>
|
||||
<div
|
||||
class="mx-auto mb-4 mt-4 w-full overflow-hidden border-y border-slate-300 shadow-md sm:mb-6 sm:mt-6 sm:max-w-md sm:rounded-md sm:border"
|
||||
>
|
||||
<div class="flex h-full items-stretch justify-center">
|
||||
<% if collection.prev_page %>
|
||||
<%= link_to_previous_page collection, raw("← Previous Page (#{collection.current_page - 1})"), class: 'hover:underline hover:bg-slate-200 hover:shadow-sm px-6 py-2 text-center border-slate-300 border-r-2 bg-slate-100 flex-1 flex items-center justify-center' %>
|
||||
<%= link_to_previous_page collection,
|
||||
raw("← Previous Page (#{collection.current_page - 1})"),
|
||||
class:
|
||||
"sky-link px-6 py-2 text-center border-slate-300 border-r-2 bg-slate-100 flex-1 flex items-center justify-center" %>
|
||||
<% else %>
|
||||
<div class='px-6 py-2 text-center border-slate-300 border-r-2 bg-slate-50 text-slate-400 flex-1 flex items-center justify-center'>
|
||||
<div
|
||||
class="flex flex-1 items-center justify-center border-r border-slate-300 bg-slate-50 px-6 py-2 text-center text-slate-400"
|
||||
>
|
||||
← Previous Page
|
||||
</div>
|
||||
<% end %>
|
||||
<% if collection.next_page %>
|
||||
<%= link_to_next_page collection, raw("Next Page (#{collection.current_page + 1}) →"), class: 'hover:underline hover:bg-slate-200 hover:shadow-sm px-6 py-2 text-center bg-slate-100 flex-1 flex items-center justify-center' %>
|
||||
<%= link_to_next_page collection,
|
||||
raw("Next Page (#{collection.current_page + 1}) →"),
|
||||
class:
|
||||
"sky-link px-6 py-2 text-center bg-slate-100 flex-1 flex items-center justify-center" %>
|
||||
<% else %>
|
||||
<div class='px-6 py-2 text-center bg-slate-50 text-slate-400 flex-1 flex items-center justify-center'>
|
||||
<div
|
||||
class="flex flex-1 items-center justify-center bg-slate-50 px-6 py-2 text-center text-slate-400"
|
||||
>
|
||||
Next Page →
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"defaults"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@4az/prettier-plugin-html-erb": "^0.0.6",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||
"@prettier/plugin-ruby": "^4.0.4",
|
||||
"@types/lodash": "^4.14.192",
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@4az/prettier-plugin-html-erb@^0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@4az/prettier-plugin-html-erb/-/prettier-plugin-html-erb-0.0.6.tgz#1040cd6e3bc488c6668b03120eb119caebc49fc7"
|
||||
integrity sha512-PB2jcgU6mIlp8tTLzDD756FcT1+lKKCCwACh/XX+hU5nEEaNsdX6eHZkznlqyw1LiFpyYGc381M44gywOpPy4Q==
|
||||
|
||||
"@alloc/quick-lru@^5.2.0":
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz"
|
||||
|
||||
Reference in New Issue
Block a user