Skip to content

Commit 35b81f7

Browse files
committed
Enhance User Profile Hero Component
- Updated UserProfileBuilders.tsx to improve the user profile hero layout, including a new cover image and avatar styling. - Added dynamic last seen and username display, with localization support for join date and member since labels. - Refactored CSS styles in UserPage.less for better responsiveness and visual consistency, including adjustments to avatar size and positioning. - Integrated user bio component with improved styling and hover effects. - Updated English and Brazilian Portuguese locale files to include new translation keys for "Member since". This commit enhances the overall user experience on the profile page by providing a more visually appealing and informative layout.
1 parent 6728762 commit 35b81f7

6 files changed

Lines changed: 223 additions & 180 deletions

File tree

js/dist/forum.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/dist/forum.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/src/forum/components/UserProfileBuilders.tsx

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import AvatarEditor from 'flarum/forum/components/AvatarEditor';
1717
import Dropdown from 'flarum/common/components/Dropdown';
1818
import listItems from 'flarum/common/helpers/listItems';
1919
import SelectDropdown from 'flarum/common/components/SelectDropdown';
20+
import humanTime from 'flarum/common/helpers/humanTime';
2021
import { safeCssUrl, trans } from '../utils';
2122

2223
// ─── Scrollable nav ───────────────────────────────────────────────────────────
@@ -134,10 +135,11 @@ export function buildHero(user: any, isEditable: boolean, controls: any[] = [])
134135
if (!user) {
135136
return (
136137
<div className="AvocadoUserPage-hero AvocadoUserPage-hero--skeleton">
138+
<div className="AvocadoUserPage-cover" />
137139
<div className="AvocadoUserPage-hero-inner">
138-
<div className="AvocadoUserPage-hero-row">
140+
<div className="AvocadoUserPage-hero-bar">
139141
<div className="AvocadoUserPage-shimmer AvocadoUserPage-shimmer--avatar" />
140-
<div style={{ flex: 1 }}>
142+
<div className="AvocadoUserPage-hero-identity">
141143
<div className="AvocadoUserPage-shimmer AvocadoUserPage-shimmer--name" />
142144
<div className="AvocadoUserPage-shimmer AvocadoUserPage-shimmer--meta" />
143145
</div>
@@ -150,8 +152,14 @@ export function buildHero(user: any, isEditable: boolean, controls: any[] = [])
150152
const color = user.color?.() || '#5a6480';
151153
const badges = user.badges?.().toArray?.() || [];
152154
const isOnline = user.isOnline?.();
155+
const lastSeen = user.lastSeenAt?.();
156+
const username = user.username?.();
153157
const joinTime = user.joinTime?.();
154-
const joinLabel = joinTime ? new Date(joinTime as string).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }) : null;
158+
159+
// Locale do navegador (undefined) em vez de 'en-US' fixo, para respeitar pt-BR.
160+
const joinLabel = joinTime
161+
? new Date(joinTime as string).toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric' })
162+
: null;
155163

156164
// forumaker/profile-cover compat — a extensão estende UserCard.prototype.view,
157165
// mas o Avocado renderiza um hero próprio (não usa UserCard aqui). Lemos
@@ -165,31 +173,62 @@ export function buildHero(user: any, isEditable: boolean, controls: any[] = [])
165173
const heroStyle: Record<string, string> = { '--user-color': color };
166174
if (hasCover) heroStyle['--user-cover'] = coverCss as string;
167175

176+
const onlineLabel = trans('ramon-avocado.forum.user.online', 'Online');
177+
178+
const bioNode = (() => {
179+
try {
180+
const UserBio = (flarum as any).reg.get('fof-user-bio', 'forum/components/UserBio');
181+
if (UserBio && user.attribute('canViewBio')) {
182+
return (
183+
<div className="AvocadoUserPage-hero-bio">
184+
<UserBio user={user} editable={isEditable} />
185+
</div>
186+
);
187+
}
188+
} catch {}
189+
return null;
190+
})();
191+
192+
// Layout estilo Facebook: capa larga, avatar grande sobreposto à esquerda,
193+
// identidade (nome + @handle + stats) à direita, ações na extrema direita —
194+
// tudo numa faixa horizontal; a bio fica abaixo, em largura cheia.
168195
return (
169196
<div className={'AvocadoUserPage-hero' + (hasCover ? ' AvocadoUserPage-hero--hasCover' : '')} style={heroStyle}>
197+
<div className="AvocadoUserPage-cover" />
170198
<div className="AvocadoUserPage-hero-inner">
171-
<div className="AvocadoUserPage-hero-row">
199+
<div className="AvocadoUserPage-hero-bar">
172200
<div className="AvocadoUserPage-hero-avatarWrap">
173201
{isEditable ? <AvatarEditor user={user} /> : <Avatar user={user} loading="eager" />}
174-
{isOnline &&
175-
(() => {
176-
const onlineLabel = trans('ramon-avocado.forum.user.online', 'Online');
177-
return <span className="AvocadoUserPage-onlineDot" title={onlineLabel} aria-label={onlineLabel} role="img" />;
178-
})()}
202+
{isOnline && <span className="AvocadoUserPage-onlineDot" title={onlineLabel} aria-label={onlineLabel} role="img" />}
179203
</div>
180-
<div className="AvocadoUserPage-hero-info">
181-
<h1 className="AvocadoUserPage-hero-name">{user.displayName?.() || user.username?.()}</h1>
182-
{badges.length > 0 && <ul className="AvocadoUserPage-hero-badges badges">{listItems(badges)}</ul>}
183-
<div className="AvocadoUserPage-hero-stats">
204+
205+
<div className="AvocadoUserPage-hero-identity">
206+
<div className="AvocadoUserPage-hero-nameRow">
207+
<h1 className="AvocadoUserPage-hero-name">{user.displayName?.() || username}</h1>
208+
{badges.length > 0 && <ul className="AvocadoUserPage-hero-badges badges">{listItems(badges)}</ul>}
209+
</div>
210+
{username && <span className="AvocadoUserPage-hero-username">@{username}</span>}
211+
212+
<div className="AvocadoUserPage-hero-meta">
213+
{/* Posts/discussões não entram aqui — os números já aparecem nas abas abaixo. */}
184214
{joinLabel && (
185-
<span className="AvocadoUserPage-hero-statPill">
186-
{app.translator.trans('ramon-avocado.forum.profile.joined', { date: joinLabel })}
215+
<span className="AvocadoUserPage-metaItem">
216+
<i className="far fa-calendar" aria-hidden="true" />
217+
{trans('ramon-avocado.forum.profile.member_since', 'Member since {date}', { date: joinLabel })}
218+
</span>
219+
)}
220+
{/* Online é indicado pelo dot no avatar; na meta só "visto há X" quando offline. */}
221+
{!isOnline && lastSeen && (
222+
<span className="AvocadoUserPage-metaItem">
223+
<i className="far fa-clock" aria-hidden="true" />
224+
{humanTime(lastSeen)}
187225
</span>
188226
)}
189227
</div>
190228
</div>
229+
191230
{controls.length > 0 && (
192-
<div className="AvocadoUserPage-hero-controls">
231+
<div className="AvocadoUserPage-hero-actions">
193232
<Dropdown
194233
buttonClassName="Button AvocadoUserPage-controlsBtn"
195234
menuClassName="Dropdown-menu--right"
@@ -200,19 +239,8 @@ export function buildHero(user: any, isEditable: boolean, controls: any[] = [])
200239
</div>
201240
)}
202241
</div>
203-
{(() => {
204-
try {
205-
const UserBio = (flarum as any).reg.get('fof-user-bio', 'forum/components/UserBio');
206-
if (UserBio && user.attribute('canViewBio')) {
207-
return (
208-
<div className="AvocadoUserPage-hero-bio">
209-
<UserBio user={user} editable={isEditable} />
210-
</div>
211-
);
212-
}
213-
} catch {}
214-
return null;
215-
})()}
242+
243+
{bioNode}
216244
</div>
217245
</div>
218246
);

0 commit comments

Comments
 (0)