Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class ApplicationController < ActionController::Base
include Pagy::Method
include UserProfileable

# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
allow_browser versions: :modern
Expand Down
22 changes: 22 additions & 0 deletions app/controllers/concerns/user_profileable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module UserProfileable
extend ActiveSupport::Concern

included do
inertia_share do
{
user_profile_id: params[:user_profile_id]&.to_i,
user_profile: load_user_profile
}.compact
end
end

private

def load_user_profile
return nil if params[:user_profile_id].blank?
sleep 1

User.find_by(id: params[:user_profile_id])
&.as_json(only: [ :username, :email, :about_me, :id, :topics_count, :messages_count ])
end
end
23 changes: 19 additions & 4 deletions app/frontend/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
import { Link, router } from '@inertiajs/react'
import { User } from '../types'

const Avatar = ({ user }: { user: User }) => {
const { username } = user;
const { username, id } = user;

if (!user) {
return null;
}

const handleClick = (url: string) => {
router.push({
url,
props: (currentProps) => ({ ...currentProps, user_profile_id: id }),
preserveState: true,
preserveScroll: true,
})
}

return (
<div
<Link
href=""
data={{ user_profile_id: id }}
only={['user_profile', 'user_profile_id']}
className="w-8 h-8 rounded-full bg-sky-100 flex items-center justify-center text-sky-600 text-xs font-medium"
title={username}
// preserveUrl // If we don't want shareable links
onBefore={(e) => handleClick(e.url.toString())}
>
{username.charAt(0).toUpperCase()}
</div>
</Link>
)
}

export default Avatar
export default Avatar
47 changes: 47 additions & 0 deletions app/frontend/components/UserProfileModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Modal from './Modal'
import {router, usePage} from '@inertiajs/react'

import LoadingSpinner from '@/components/LoadingSpinner';
import UserProfile from '@/components/UserProfile';
import type {User} from '@/types'

export default function UserProfileModal() {
const {props: {user_profile_id: userProfileId, user_profile: userProfile}} = usePage<{
user_profile_id: number,
user_profile: User
}>()

const isLoading = userProfileId && !userProfile;

const handleClose = () => {
const pathname = window.location.pathname;
const params = new URLSearchParams(window.location.search);
params.delete('user_profile_id');

const queryString = params.toString();
const url = queryString ? `${pathname}?${queryString}` : pathname;

router.visit(url, {
preserveScroll: true,
preserveState: true,
onBefore: (e) => {
router.push({
url: e.url.toString(),
props: (currentProps) => ({...currentProps, user_profile_id: null}),
preserveState: true,
preserveScroll: true,
})
}
})
}

return (
<Modal isOpen={!!userProfileId} onClose={handleClose} title='User Profile'>
{isLoading ? (
<LoadingSpinner/>
) : (
<UserProfile user={userProfile} onClose={handleClose}/>
)}
</Modal>
)
}
3 changes: 3 additions & 0 deletions app/frontend/layouts/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
MagnifyingGlassIcon,
ChevronDownIcon
} from '@heroicons/react/24/outline'

import { User } from '@/types'
import Sidebar from "@/components/Sidebar";
import UserProfileModal from '../components/UserProfileModal'

interface AppLayoutProps {
children: ReactNode
Expand Down Expand Up @@ -95,6 +97,7 @@ export default function AppLayout({ children }: AppLayoutProps) {
{children}
</main>
</div>
<UserProfileModal />
</div>
)
}