Update frontend assets handling
This commit is contained in:
parent
713a35ab75
commit
642fc2d196
20
src/App.jsx
20
src/App.jsx
|
|
@ -30,6 +30,24 @@ export default function App() {
|
||||||
setWallError(next.error || "");
|
setWallError(next.error || "");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handlePostDeleted = useCallback((postId) => {
|
||||||
|
setWallPosts((prev) => prev.filter((p) => (p.id ?? p._id) !== postId));
|
||||||
|
if (selectedPost && (selectedPost.id ?? selectedPost._id) === postId) {
|
||||||
|
setSelectedPost(null);
|
||||||
|
}
|
||||||
|
}, [selectedPost]);
|
||||||
|
|
||||||
|
const handlePostUpdated = useCallback((updatedPost) => {
|
||||||
|
if (!updatedPost) return;
|
||||||
|
setWallPosts((prev) =>
|
||||||
|
prev.map((p) =>
|
||||||
|
(p.id ?? p._id) === (updatedPost.id ?? updatedPost._id)
|
||||||
|
? { ...p, ...updatedPost }
|
||||||
|
: p
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// charge le thème au démarrage
|
// charge le thème au démarrage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window === "undefined") return;
|
if (typeof window === "undefined") return;
|
||||||
|
|
@ -186,6 +204,8 @@ export default function App() {
|
||||||
error={wallError}
|
error={wallError}
|
||||||
selectedPost={selectedPost}
|
selectedPost={selectedPost}
|
||||||
onSelectPost={setSelectedPost}
|
onSelectPost={setSelectedPost}
|
||||||
|
onPostDeleted={handlePostDeleted}
|
||||||
|
onPostUpdated={handlePostUpdated}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* CHAT PANEL SUPPRIMÉ */}
|
{/* CHAT PANEL SUPPRIMÉ */}
|
||||||
|
|
|
||||||
|
|
@ -225,3 +225,117 @@
|
||||||
background: rgba(248,113,113,0.25);
|
background: rgba(248,113,113,0.25);
|
||||||
border-color: rgba(248,113,113,0.7);
|
border-color: rgba(248,113,113,0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add Friend Form */
|
||||||
|
.friends-add-form {
|
||||||
|
display: flex;
|
||||||
|
gap: .25rem;
|
||||||
|
margin-bottom: .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: .35rem .5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(71, 85, 105, 0.5);
|
||||||
|
background: rgba(15, 23, 42, 0.8);
|
||||||
|
color: #e5e7eb;
|
||||||
|
font-size: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-input::placeholder {
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: rgba(56, 189, 248, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-btn {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(56, 189, 248, 0.4);
|
||||||
|
background: rgba(56, 189, 248, 0.15);
|
||||||
|
color: #7dd3fc;
|
||||||
|
font-size: .8rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-btn:hover:not(:disabled) {
|
||||||
|
background: rgba(56, 189, 248, 0.25);
|
||||||
|
border-color: rgba(56, 189, 248, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-error {
|
||||||
|
font-size: .7rem;
|
||||||
|
color: #fca5a5;
|
||||||
|
padding: .2rem .3rem;
|
||||||
|
margin-bottom: .3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-add-success {
|
||||||
|
font-size: .7rem;
|
||||||
|
color: #4ade80;
|
||||||
|
padding: .2rem .3rem;
|
||||||
|
margin-bottom: .3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suggestions dropdown */
|
||||||
|
.friends-add-wrap {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: .4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-suggestions {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(15, 23, 42, 0.98);
|
||||||
|
border: 1px solid rgba(56, 189, 248, 0.4);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 4px;
|
||||||
|
max-height: 180px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-suggestion-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .4rem;
|
||||||
|
padding: .4rem .5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: .75rem;
|
||||||
|
color: #e5e7eb;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-suggestion-item:hover {
|
||||||
|
background: rgba(56, 189, 248, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-suggestion-item .friend-avatar {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: .65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-suggestions-loading {
|
||||||
|
font-size: .7rem;
|
||||||
|
color: #64748b;
|
||||||
|
padding: .3rem .4rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
fetchFriends,
|
fetchFriends,
|
||||||
fetchFriendRequests,
|
fetchFriendRequests,
|
||||||
acceptFriendRequest,
|
acceptFriendRequest,
|
||||||
rejectFriendRequest,
|
rejectFriendRequest,
|
||||||
removeFriend,
|
removeFriend,
|
||||||
|
sendFriendRequest,
|
||||||
} from '../../api/client';
|
} from '../../api/client';
|
||||||
import { useAuth } from '../../auth/AuthContext';
|
import { useAuth } from '../../auth/AuthContext';
|
||||||
|
import { recoSearch } from '../../services/recoSearch';
|
||||||
import './Friends.css';
|
import './Friends.css';
|
||||||
|
|
||||||
export default function FriendsList({ onSelectUser, onClose }) {
|
export default function FriendsList({ onSelectUser, onClose }) {
|
||||||
|
|
@ -16,6 +18,19 @@ export default function FriendsList({ onSelectUser, onClose }) {
|
||||||
const [requests, setRequests] = useState([]);
|
const [requests, setRequests] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
// Add friend state
|
||||||
|
const [addUsername, setAddUsername] = useState('');
|
||||||
|
const [addLoading, setAddLoading] = useState(false);
|
||||||
|
const [addError, setAddError] = useState('');
|
||||||
|
const [addSuccess, setAddSuccess] = useState('');
|
||||||
|
|
||||||
|
// Autocomplete state
|
||||||
|
const [suggestions, setSuggestions] = useState([]);
|
||||||
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||||
|
const [suggestionsLoading, setSuggestionsLoading] = useState(false);
|
||||||
|
const searchTimeoutRef = useRef(null);
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!authenticated) return;
|
if (!authenticated) return;
|
||||||
|
|
||||||
|
|
@ -33,6 +48,58 @@ export default function FriendsList({ onSelectUser, onClose }) {
|
||||||
loadData();
|
loadData();
|
||||||
}, [authenticated]);
|
}, [authenticated]);
|
||||||
|
|
||||||
|
// Search for users with debounce
|
||||||
|
const searchUsers = async (query) => {
|
||||||
|
if (!query || query.length < 2) {
|
||||||
|
setSuggestions([]);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSuggestionsLoading(true);
|
||||||
|
try {
|
||||||
|
const results = await recoSearch(query, { limit: 8 });
|
||||||
|
// Filter only profiles
|
||||||
|
const profiles = results.filter(
|
||||||
|
(r) => r.type === 'profile' || r.kind === 'profile'
|
||||||
|
);
|
||||||
|
setSuggestions(profiles);
|
||||||
|
setShowSuggestions(profiles.length > 0);
|
||||||
|
} catch {
|
||||||
|
setSuggestions([]);
|
||||||
|
}
|
||||||
|
setSuggestionsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
setAddUsername(value);
|
||||||
|
|
||||||
|
// Debounce search
|
||||||
|
if (searchTimeoutRef.current) {
|
||||||
|
clearTimeout(searchTimeoutRef.current);
|
||||||
|
}
|
||||||
|
searchTimeoutRef.current = setTimeout(() => {
|
||||||
|
searchUsers(value);
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectSuggestion = (username) => {
|
||||||
|
setAddUsername(username);
|
||||||
|
setSuggestions([]);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
inputRef.current?.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cleanup timeout on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (searchTimeoutRef.current) {
|
||||||
|
clearTimeout(searchTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleAccept = async (username) => {
|
const handleAccept = async (username) => {
|
||||||
const res = await acceptFriendRequest(username);
|
const res = await acceptFriendRequest(username);
|
||||||
if (res?.ok) {
|
if (res?.ok) {
|
||||||
|
|
@ -58,6 +125,28 @@ export default function FriendsList({ onSelectUser, onClose }) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddFriend = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const uname = addUsername.trim();
|
||||||
|
if (!uname) return;
|
||||||
|
|
||||||
|
setAddLoading(true);
|
||||||
|
setAddError('');
|
||||||
|
setAddSuccess('');
|
||||||
|
|
||||||
|
const res = await sendFriendRequest(uname);
|
||||||
|
setAddLoading(false);
|
||||||
|
|
||||||
|
if (res?.ok) {
|
||||||
|
setAddSuccess(`Friend request sent to ${uname}`);
|
||||||
|
setAddUsername('');
|
||||||
|
setTimeout(() => setAddSuccess(''), 3000);
|
||||||
|
} else {
|
||||||
|
setAddError(res?.error || res?.text || 'Could not send request');
|
||||||
|
setTimeout(() => setAddError(''), 4000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
return (
|
return (
|
||||||
<div className="friends-panel">
|
<div className="friends-panel">
|
||||||
|
|
@ -92,6 +181,53 @@ export default function FriendsList({ onSelectUser, onClose }) {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Add Friend Form */}
|
||||||
|
<div className="friends-add-wrap">
|
||||||
|
<form className="friends-add-form" onSubmit={handleAddFriend}>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
className="friends-add-input"
|
||||||
|
placeholder="Add friend by username..."
|
||||||
|
value={addUsername}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onFocus={() => suggestions.length > 0 && setShowSuggestions(true)}
|
||||||
|
onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
|
||||||
|
disabled={addLoading}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="friends-add-btn"
|
||||||
|
disabled={addLoading || !addUsername.trim()}
|
||||||
|
title="Send friend request"
|
||||||
|
>
|
||||||
|
{addLoading ? '...' : <i className="fa-solid fa-user-plus" />}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{/* Suggestions dropdown */}
|
||||||
|
{showSuggestions && suggestions.length > 0 && (
|
||||||
|
<div className="friends-suggestions">
|
||||||
|
{suggestions.map((s) => (
|
||||||
|
<div
|
||||||
|
key={s.id}
|
||||||
|
className="friends-suggestion-item"
|
||||||
|
onMouseDown={() => handleSelectSuggestion(s.title || s.raw?.username || s.id)}
|
||||||
|
>
|
||||||
|
<div className="friend-avatar">
|
||||||
|
{(s.title || s.id)[0]?.toUpperCase() || '?'}
|
||||||
|
</div>
|
||||||
|
<span>{s.title || s.raw?.username || s.id}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{suggestionsLoading && <div className="friends-suggestions-loading">Searching...</div>}
|
||||||
|
</div>
|
||||||
|
{addError && <div className="friends-add-error">{addError}</div>}
|
||||||
|
{addSuccess && <div className="friends-add-success">{addSuccess}</div>}
|
||||||
|
|
||||||
<div className="friends-content">
|
<div className="friends-content">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="friends-loading">Loading...</div>
|
<div className="friends-loading">Loading...</div>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useAuth } from "../../auth/AuthContext";
|
import { useAuth } from "../../auth/AuthContext";
|
||||||
import { registerUser, updateProfileUsername } from "../../api/client";
|
import { registerUser, updateProfileUsername, fetchFriendRequests } from "../../api/client";
|
||||||
import AuthToast from "../Auth/AuthToast";
|
import AuthToast from "../Auth/AuthToast";
|
||||||
import { promptInstall, isRunningAsPWA, canInstall } from '../../pwaInstall';
|
import { promptInstall, isRunningAsPWA, canInstall } from '../../pwaInstall';
|
||||||
import { trackEvent } from "../../utils/analytics";
|
import { trackEvent } from "../../utils/analytics";
|
||||||
|
import FriendsList from "../Friends/FriendsList";
|
||||||
|
|
||||||
const THEME_LABELS = { dark: "Dark", blue: "Blue", light: "Light" };
|
const THEME_LABELS = { dark: "Dark", blue: "Blue", light: "Light" };
|
||||||
const LAST_SIGNUP_KEY = "sociowire:lastSignup";
|
const LAST_SIGNUP_KEY = "sociowire:lastSignup";
|
||||||
|
|
@ -77,6 +78,8 @@ export default function TopBar({ theme = "dark", onChangeTheme, onIslandsClick,
|
||||||
const [showAuth, setShowAuth] = useState(false);
|
const [showAuth, setShowAuth] = useState(false);
|
||||||
const [mode, setMode] = useState("login");
|
const [mode, setMode] = useState("login");
|
||||||
const [showUsernameModal, setShowUsernameModal] = useState(false);
|
const [showUsernameModal, setShowUsernameModal] = useState(false);
|
||||||
|
const [showFriends, setShowFriends] = useState(false);
|
||||||
|
const [pendingRequestsCount, setPendingRequestsCount] = useState(0);
|
||||||
const [desiredUsername, setDesiredUsername] = useState("");
|
const [desiredUsername, setDesiredUsername] = useState("");
|
||||||
const [usernameError, setUsernameError] = useState("");
|
const [usernameError, setUsernameError] = useState("");
|
||||||
const [usernameInfo, setUsernameInfo] = useState("");
|
const [usernameInfo, setUsernameInfo] = useState("");
|
||||||
|
|
@ -162,6 +165,22 @@ export default function TopBar({ theme = "dark", onChangeTheme, onIslandsClick,
|
||||||
if (authenticated) setShowAuth(false);
|
if (authenticated) setShowAuth(false);
|
||||||
}, [authenticated]);
|
}, [authenticated]);
|
||||||
|
|
||||||
|
// Fetch pending friend requests count
|
||||||
|
useEffect(() => {
|
||||||
|
if (!authenticated) {
|
||||||
|
setPendingRequestsCount(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const loadRequests = async () => {
|
||||||
|
const res = await fetchFriendRequests();
|
||||||
|
setPendingRequestsCount(res?.requests?.length || 0);
|
||||||
|
};
|
||||||
|
loadRequests();
|
||||||
|
// Refresh every 30 seconds
|
||||||
|
const interval = setInterval(loadRequests, 30000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [authenticated]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
setStableUsername("");
|
setStableUsername("");
|
||||||
|
|
@ -477,6 +496,21 @@ export default function TopBar({ theme = "dark", onChangeTheme, onIslandsClick,
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Friends button - only when authenticated */}
|
||||||
|
{authenticated && (
|
||||||
|
<button
|
||||||
|
className={"btn-action btn-action-friends" + (showFriends ? " is-active" : "")}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowFriends(!showFriends)}
|
||||||
|
title="Friends"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-user-group"></i>
|
||||||
|
{pendingRequestsCount > 0 && (
|
||||||
|
<span className="friends-badge-count">{pendingRequestsCount}</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Login/Logout button */}
|
{/* Login/Logout button */}
|
||||||
<button
|
<button
|
||||||
className="btn-action btn-action-auth"
|
className="btn-action btn-action-auth"
|
||||||
|
|
@ -664,6 +698,19 @@ export default function TopBar({ theme = "dark", onChangeTheme, onIslandsClick,
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{/* Friends Panel - dropdown from topbar */}
|
||||||
|
{showFriends && (
|
||||||
|
<div className="friends-dropdown-wrap">
|
||||||
|
<FriendsList
|
||||||
|
onSelectUser={(uname) => {
|
||||||
|
setShowFriends(false);
|
||||||
|
if (onUserIsland) onUserIsland(uname);
|
||||||
|
}}
|
||||||
|
onClose={() => setShowFriends(false)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1275,8 +1275,9 @@ export default function MapView({
|
||||||
const variants = data && data.variants ? data.variants : null;
|
const variants = data && data.variants ? data.variants : null;
|
||||||
const pickRef = (v) => {
|
const pickRef = (v) => {
|
||||||
if (!v) return "";
|
if (!v) return "";
|
||||||
|
if (v.url) return v.url;
|
||||||
if (v.asset_id) return `asset://${v.asset_id}`;
|
if (v.asset_id) return `asset://${v.asset_id}`;
|
||||||
return v.url || "";
|
return "";
|
||||||
};
|
};
|
||||||
const smallRef = variants ? pickRef(variants.small) : "";
|
const smallRef = variants ? pickRef(variants.small) : "";
|
||||||
const mediumRef = variants ? pickRef(variants.medium) : "";
|
const mediumRef = variants ? pickRef(variants.medium) : "";
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ function getKmFromPost(post) {
|
||||||
return post.km ?? post.distance_km ?? post.distanceKm ?? post.dist_km ?? null;
|
return post.km ?? post.distance_km ?? post.distanceKm ?? post.dist_km ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PostList({ posts, loading, error, selectedPost, onSelectPost }) {
|
export default function PostList({ posts, loading, error, selectedPost, onSelectPost, onPostDeleted, onPostUpdated }) {
|
||||||
const [kmFilter, setKmFilter] = useState(1000);
|
const [kmFilter, setKmFilter] = useState(1000);
|
||||||
const [visibleCount, setVisibleCount] = useState(6);
|
const [visibleCount, setVisibleCount] = useState(6);
|
||||||
const listRef = useRef(null);
|
const listRef = useRef(null);
|
||||||
|
|
@ -66,6 +66,8 @@ export default function PostList({ posts, loading, error, selectedPost, onSelect
|
||||||
post={p}
|
post={p}
|
||||||
selected={selectedPost && (selectedPost.id ?? selectedPost._id) === (p.id ?? p._id)}
|
selected={selectedPost && (selectedPost.id ?? selectedPost._id) === (p.id ?? p._id)}
|
||||||
onSelect={onSelectPost}
|
onSelect={onSelectPost}
|
||||||
|
onPostDeleted={onPostDeleted}
|
||||||
|
onPostUpdated={onPostUpdated}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -715,3 +715,79 @@ body[data-theme="light"] .sw-avatar{
|
||||||
.brand-under{ padding-left: 0; }
|
.brand-under{ padding-left: 0; }
|
||||||
}
|
}
|
||||||
/* SW_TOPBAR_ONE_LINE_FIX:END */
|
/* SW_TOPBAR_ONE_LINE_FIX:END */
|
||||||
|
|
||||||
|
/* SW_FRIENDS_BUTTON:BEGIN */
|
||||||
|
.btn-action-friends{
|
||||||
|
background: linear-gradient(135deg, #f59e0b, #d97706);
|
||||||
|
border-color: rgba(245, 158, 11, 0.5);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action-friends:hover{
|
||||||
|
background: linear-gradient(135deg, #fbbf24, #f59e0b);
|
||||||
|
box-shadow: 0 4px 16px rgba(245, 158, 11, 0.6);
|
||||||
|
border-color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action-friends.is-active{
|
||||||
|
background: linear-gradient(135deg, #fbbf24, #f59e0b);
|
||||||
|
box-shadow: 0 4px 16px rgba(245, 158, 11, 0.6);
|
||||||
|
border-color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action-friends{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.friends-badge-count{
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
right: -6px;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #ef4444;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
font-weight: 700;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 2px solid rgba(15, 23, 42, 0.9);
|
||||||
|
box-shadow: 0 2px 6px rgba(239, 68, 68, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Friends dropdown panel positioning */
|
||||||
|
.friends-dropdown-wrap{
|
||||||
|
position: fixed;
|
||||||
|
top: 70px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 200;
|
||||||
|
animation: friendsSlideIn 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes friendsSlideIn{
|
||||||
|
from{
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to{
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px){
|
||||||
|
.friends-dropdown-wrap{
|
||||||
|
top: auto;
|
||||||
|
bottom: 80px;
|
||||||
|
right: 10px;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
.friends-dropdown-wrap .friends-panel{
|
||||||
|
max-width: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* SW_FRIENDS_BUTTON:END */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue