Refactor socket handling across components to support dual socket connections, enhancing data fetching capabilities and improving overall communication.

This commit is contained in:
seb
2025-07-03 05:36:41 +02:00
parent 1ed06804a0
commit 245f5067ed
19 changed files with 113 additions and 37 deletions

View File

@@ -165,7 +165,8 @@ const AppContent = ({ currentTheme, onThemeChange }) => {
// Check if we're in development mode // Check if we're in development mode
const isDevelopment = process.env.NODE_ENV === "development"; const isDevelopment = process.env.NODE_ENV === "development";
const socket = useContext(SocketContext); const {socket,socketB} = useContext(SocketContext);
console.log("AppContent: socket", socket);
return ( return (
<Box <Box
@@ -201,7 +202,7 @@ const AppContent = ({ currentTheme, onThemeChange }) => {
{/* Category page - Render Content in parallel */} {/* Category page - Render Content in parallel */}
<Route <Route
path="/Kategorie/:categoryId" path="/Kategorie/:categoryId"
element={<Content socket={socket} />} element={<Content socket={socket} socketB={socketB} />}
/> />
{/* Single product page */} {/* Single product page */}
<Route <Route
@@ -210,7 +211,7 @@ const AppContent = ({ currentTheme, onThemeChange }) => {
/> />
{/* Search page - Render Content in parallel */} {/* Search page - Render Content in parallel */}
<Route path="/search" element={<Content socket={socket} />} /> <Route path="/search" element={<Content socket={socket} socketB={socketB} />} />
{/* Profile page */} {/* Profile page */}
<Route path="/profile" element={<ProfilePageWithSocket />} /> <Route path="/profile" element={<ProfilePageWithSocket />} />
@@ -218,17 +219,17 @@ const AppContent = ({ currentTheme, onThemeChange }) => {
{/* Reset password page */} {/* Reset password page */}
<Route <Route
path="/resetPassword" path="/resetPassword"
element={<ResetPassword socket={socket} />} element={<ResetPassword socket={socket} socketB={socketB} />}
/> />
{/* Admin page */} {/* Admin page */}
<Route path="/admin" element={<AdminPage socket={socket} />} /> <Route path="/admin" element={<AdminPage socket={socket} socketB={socketB} />} />
{/* Admin Users page */} {/* Admin Users page */}
<Route path="/admin/users" element={<UsersPage socket={socket} />} /> <Route path="/admin/users" element={<UsersPage socket={socket} socketB={socketB} />} />
{/* Admin Server Logs page */} {/* Admin Server Logs page */}
<Route path="/admin/logs" element={<ServerLogsPage socket={socket} />} /> <Route path="/admin/logs" element={<ServerLogsPage socket={socket} socketB={socketB} />} />
{/* Legal pages */} {/* Legal pages */}
<Route path="/datenschutz" element={<Datenschutz />} /> <Route path="/datenschutz" element={<Datenschutz />} />

View File

@@ -22,7 +22,7 @@ const CategoryBox = ({
const [imageUrl, setImageUrl] = useState(null); const [imageUrl, setImageUrl] = useState(null);
const [imageError, setImageError] = useState(false); const [imageError, setImageError] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const socket = useContext(SocketContext); const {socket} = useContext(SocketContext);
useEffect(() => { useEffect(() => {
let objectUrl = null; let objectUrl = null;

View File

@@ -685,6 +685,7 @@ class Content extends Component {
<Box> <Box>
<ProductList <ProductList
socket={this.props.socket} socket={this.props.socket}
socketB={this.props.socketB}
totalProductCount={(this.state.unfilteredProducts || []).length} totalProductCount={(this.state.unfilteredProducts || []).length}
products={this.state.filteredProducts || []} products={this.state.filteredProducts || []}
activeAttributeFilters={this.state.activeAttributeFilters || []} activeAttributeFilters={this.state.activeAttributeFilters || []}

View File

@@ -37,7 +37,7 @@ class Header extends Component {
render() { render() {
// Get socket directly from context in render method // Get socket directly from context in render method
const socket = this.context; const {socket,socketB} = this.context;
const { isHomePage, isProfilePage } = this.props; const { isHomePage, isProfilePage } = this.props;
return ( return (
@@ -77,7 +77,7 @@ class Header extends Component {
</Box> </Box>
</Container> </Container>
</Toolbar> </Toolbar>
{(isHomePage || this.props.categoryId || isProfilePage) && <CategoryList categoryId={209} activeCategoryId={this.props.categoryId} socket={socket} />} {(isHomePage || this.props.categoryId || isProfilePage) && <CategoryList categoryId={209} activeCategoryId={this.props.categoryId} socket={socket} socketB={socketB} />}
</AppBar> </AppBar>
); );
} }
@@ -91,7 +91,7 @@ const HeaderWithContext = (props) => {
return ( return (
<SocketContext.Consumer> <SocketContext.Consumer>
{socket => <Header {...props} socket={socket} isHomePage={isHomePage} isProfilePage={isProfilePage} />} {({socket,socketB}) => <Header {...props} socket={socket} socketB={socketB} isHomePage={isHomePage} isProfilePage={isProfilePage} />}
</SocketContext.Consumer> </SocketContext.Consumer>
); );
}; };

View File

@@ -77,7 +77,7 @@ class Images extends Component {
} }
loadPic = (size,bildId,index) => { loadPic = (size,bildId,index) => {
this.props.socket.emit('getPic', { bildId, size }, (res) => { this.props.socketB.emit('getPic', { bildId, size }, (res) => {
if(res.success){ if(res.success){
const url = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' })); const url = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' }));

View File

@@ -26,7 +26,8 @@ class Product extends Component {
this.state = {image:window.smallPicCache[bildId],loading:false, error: false} this.state = {image:window.smallPicCache[bildId],loading:false, error: false}
}else{ }else{
this.state = {image: null, loading: true, error: false}; this.state = {image: null, loading: true, error: false};
this.props.socket.emit('getPic', { bildId, size:'small' }, (res) => { console.log("Product: Fetching image from socketB", this.props.socketB);
this.props.socketB.emit('getPic', { bildId, size:'small' }, (res) => {
if(res.success){ if(res.success){
window.smallPicCache[bildId] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' })); window.smallPicCache[bildId] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' }));
if (this._isMounted) { if (this._isMounted) {

View File

@@ -110,8 +110,8 @@ class ProductDetailPage extends Component {
} }
} else { } else {
// Not in cache, fetch from server // Not in cache, fetch from server
if (this.props.socket && this.props.socket.connected) { if (this.props.socketB && this.props.socketB.connected) {
this.props.socket.emit( this.props.socketB.emit(
"getAttributePicture", "getAttributePicture",
{ id: cacheKey }, { id: cacheKey },
(res) => { (res) => {
@@ -334,6 +334,7 @@ class ProductDetailPage extends Component {
{product.pictureList && ( {product.pictureList && (
<Images <Images
socket={this.props.socket} socket={this.props.socket}
socketB={this.props.socketB}
pictureList={product.pictureList} pictureList={product.pictureList}
fullscreenOpen={this.state.imageDialogOpen} fullscreenOpen={this.state.imageDialogOpen}
onOpenFullscreen={this.handleOpenDialog} onOpenFullscreen={this.handleOpenDialog}

View File

@@ -11,7 +11,7 @@ const ProductDetailWithSocket = () => {
return ( return (
<SocketContext.Consumer> <SocketContext.Consumer>
{socket => <ProductDetailPage seoName={seoName} navigate={navigate} location={location} socket={socket} />} {({socket,socketB}) => <ProductDetailPage seoName={seoName} navigate={navigate} location={location} socket={socket} socketB={socketB} />}
</SocketContext.Consumer> </SocketContext.Consumer>
); );
}; };

View File

@@ -331,6 +331,7 @@ class ProductList extends Component {
versandklasse={product.versandklasse} versandklasse={product.versandklasse}
weight={product.weight} weight={product.weight}
socket={this.props.socket} socket={this.props.socket}
socketB={this.props.socketB}
pictureList={product.pictureList} pictureList={product.pictureList}
availableSupplier={product.availableSupplier} availableSupplier={product.availableSupplier}
/> />

View File

@@ -124,11 +124,13 @@ class CategoryList extends Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
// Handle socket connection changes // Handle socket connection changes
const wasConnected = prevProps.socket && prevProps.socket.connected; const wasConnected = prevProps.socket && prevProps.socket.connected;
const isNowConnected = this.props.socket && this.props.socket.connected; const isNowConnected = this.props.socket && this.props.socket.connected;
if (!wasConnected && isNowConnected && !this.state.fetchedCategories) { if (!wasConnected && isNowConnected && !this.state.fetchedCategories) {
// Socket just connected and we haven't fetched categories yet // Socket just connected and we haven't fetched categories yet
this.setState( this.setState(
{ {
fetchedCategories: false, fetchedCategories: false,
@@ -158,7 +160,7 @@ class CategoryList extends Component {
} }
if (this.state.fetchedCategories) { if (this.state.fetchedCategories) {
//console.log('Categories already fetched, skipping'); console.log('Categories already fetched, skipping');
return; return;
} }
@@ -222,7 +224,6 @@ class CategoryList extends Component {
//console.log('CategoryList: Fetching categories from socket'); //console.log('CategoryList: Fetching categories from socket');
socket.emit("categoryList", { categoryId: 209 }, (response) => { socket.emit("categoryList", { categoryId: 209 }, (response) => {
if (response && response.categoryTree) { if (response && response.categoryTree) {
//console.log('Category tree received:', response.categoryTree);
// Store in global cache with timestamp // Store in global cache with timestamp
try { try {
@@ -237,7 +238,6 @@ class CategoryList extends Component {
} catch (err) { } catch (err) {
console.error("Error writing to cache:", err); console.error("Error writing to cache:", err);
} }
this.processCategoryTree(response.categoryTree); this.processCategoryTree(response.categoryTree);
} else { } else {
try { try {

View File

@@ -15,7 +15,7 @@ import SocketContext from "../../contexts/SocketContext.js";
const SearchBar = () => { const SearchBar = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const socket = React.useContext(SocketContext); const {socket} = React.useContext(SocketContext);
const searchParams = new URLSearchParams(location.search); const searchParams = new URLSearchParams(location.search);
// State management // State management

View File

@@ -67,8 +67,8 @@ class CartTab extends Component {
// @note Add method to fetch and apply order template prefill data // @note Add method to fetch and apply order template prefill data
fetchOrderTemplate = () => { fetchOrderTemplate = () => {
if (this.context && this.context.connected) { if (this.context && this.context.socket && this.context.socket.connected) {
this.context.emit('getOrderTemplate', (response) => { this.context.socket.emit('getOrderTemplate', (response) => {
if (response.success && response.orderTemplate) { if (response.success && response.orderTemplate) {
const template = response.orderTemplate; const template = response.orderTemplate;
@@ -433,7 +433,7 @@ class CartTab extends Component {
{!showPaymentConfirmation && ( {!showPaymentConfirmation && (
<CartDropdown <CartDropdown
cartItems={cartItems} cartItems={cartItems}
socket={this.context} socket={this.context.socket}
showDetailedSummary={showStripePayment} showDetailedSummary={showStripePayment}
deliveryMethod={deliveryMethod} deliveryMethod={deliveryMethod}
deliveryCost={deliveryCost} deliveryCost={deliveryCost}

View File

@@ -120,7 +120,7 @@ class OrderProcessingService {
// If socket is ready, process immediately // If socket is ready, process immediately
const context = this.getContext(); const context = this.getContext();
if (context && context.connected) { if (context && context.socket && context.socket.connected) {
const { isLoggedIn: isAuthenticated } = isUserLoggedIn(); const { isLoggedIn: isAuthenticated } = isUserLoggedIn();
if (isAuthenticated) { if (isAuthenticated) {
this.sendStripeOrder(); this.sendStripeOrder();
@@ -131,7 +131,7 @@ class OrderProcessingService {
// Wait for socket to be ready // Wait for socket to be ready
this.socketHandler = () => { this.socketHandler = () => {
const context = this.getContext(); const context = this.getContext();
if (context && context.connected) { if (context && context.socket && context.socket.connected) {
const { isLoggedIn: isAuthenticated } = isUserLoggedIn(); const { isLoggedIn: isAuthenticated } = isUserLoggedIn();
const state = this.getState(); const state = this.getState();
@@ -189,7 +189,7 @@ class OrderProcessingService {
// Emit stripe order to backend via socket.io // Emit stripe order to backend via socket.io
const context = this.getContext(); const context = this.getContext();
context.emit("issueStripeOrder", orderData, (response) => { context.socket.emit("issueStripeOrder", orderData, (response) => {
if (response.success) { if (response.success) {
this.setState({ this.setState({
isCompletingOrder: false, isCompletingOrder: false,
@@ -208,8 +208,8 @@ class OrderProcessingService {
// Process regular (non-Stripe) orders // Process regular (non-Stripe) orders
processRegularOrder(orderData) { processRegularOrder(orderData) {
const context = this.getContext(); const context = this.getContext();
if (context) { if (context && context.socket && context.socket.connected) {
context.emit("issueOrder", orderData, (response) => { context.socket.emit("issueOrder", orderData, (response) => {
if (response.success) { if (response.success) {
// Clear the cart // Clear the cart
window.cart = []; window.cart = [];
@@ -246,8 +246,8 @@ class OrderProcessingService {
// Create Stripe payment intent // Create Stripe payment intent
createStripeIntent(totalAmount, loadStripeComponent) { createStripeIntent(totalAmount, loadStripeComponent) {
const context = this.getContext(); const context = this.getContext();
if (context) { if (context && context.socket && context.socket.connected) {
context.emit( context.socket.emit(
"createStripeIntent", "createStripeIntent",
{ amount: totalAmount }, { amount: totalAmount },
(response) => { (response) => {

View File

@@ -68,7 +68,7 @@ const OrdersTab = ({ orderIdFromHash }) => {
const [selectedOrder, setSelectedOrder] = useState(null); const [selectedOrder, setSelectedOrder] = useState(null);
const [isDetailsDialogOpen, setIsDetailsDialogOpen] = useState(false); const [isDetailsDialogOpen, setIsDetailsDialogOpen] = useState(false);
const socket = useContext(SocketContext); const {socket} = useContext(SocketContext);
const navigate = useNavigate(); const navigate = useNavigate();
const handleViewDetails = useCallback( const handleViewDetails = useCallback(

View File

@@ -151,7 +151,7 @@ const Home = () => {
const [rootCategories, setRootCategories] = useState(() => const [rootCategories, setRootCategories] = useState(() =>
initializeCategories() initializeCategories()
); );
const socket = useContext(SocketContext); const {socket} = useContext(SocketContext);
useEffect(() => { useEffect(() => {
// Only fetch from socket if we don't already have categories and we're in browser // Only fetch from socket if we don't already have categories and we're in browser

View File

@@ -225,8 +225,8 @@ const ProfilePage = (props) => {
// Wrap with socket context // Wrap with socket context
const ProfilePageWithSocket = (props) => { const ProfilePageWithSocket = (props) => {
const socket = useContext(SocketContext); const {socket,socketB} = useContext(SocketContext);
return <ProfilePage {...props} socket={socket} />; return <ProfilePage {...props} socket={socket} socketB={socketB} />;
}; };
export default ProfilePageWithSocket; export default ProfilePageWithSocket;

View File

@@ -36,7 +36,8 @@ const collectAllCategories = (categoryNode, categories = [], level = 0) => {
const Sitemap = () => { const Sitemap = () => {
const [categories, setCategories] = useState([]); const [categories, setCategories] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const socket = useContext(SocketContext); const {socket} = useContext(SocketContext);
const sitemapLinks = [ const sitemapLinks = [
{ title: 'Startseite', url: '/' }, { title: 'Startseite', url: '/' },

View File

@@ -7,8 +7,10 @@ class SocketProvider extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.socket = null; this.socket = null;
this.socketB = null;
this.state = { this.state = {
connected: false, connected: false,
connectedB: false,
showPrerenderFallback: true, showPrerenderFallback: true,
}; };
} }
@@ -77,6 +79,66 @@ class SocketProvider extends Component {
console.error("SocketProvider: Failed to reconnect"); console.error("SocketProvider: Failed to reconnect");
this.handleConnectionFailure(); this.handleConnectionFailure();
}); });
this.socketB = io(url, {
transports: ["websocket"],
});
this.socketB.on("connect", () => {
console.log("SocketProvider: connectedB");
//this.setState({ connectedB: true });
});
this.socketB.on("disconnect", () => {
//this.setState({ connectedB: false });
console.log("SocketProvider: Socket disconnectedB");
});
this.socketB.on("connect_error", (error) => {
console.error("SocketProvider: Connection errorB:", error);
});
this.socketB.on("reconnect_attempt", (attemptNumber) => {
console.log(`SocketProvider: Reconnection attemptB ${attemptNumber}`);
});
this.socketB.on("reconnect_failed", () => {
console.error("SocketProvider: Failed to reconnectB");
});
this.socketB.waitForConnect = (timeout = 10000) => {
return new Promise((resolve, reject) => {
if (this.socketB.connected) {
resolve();
return;
}
let timeoutId;
const connectHandler = () => {
clearTimeout(timeoutId);
this.socketB.off("connect", connectHandler);
this.socketB.off("connect_error", errorHandler);
resolve();
};
const errorHandler = (error) => {
clearTimeout(timeoutId);
this.socketB.off("connect", connectHandler);
this.socketB.off("connect_error", errorHandler);
reject(new Error(`Socket connection failed: ${error.message}`));
};
// Set up timeout
timeoutId = setTimeout(() => {
this.socketB.off("connect", connectHandler);
this.socketB.off("connect_error", errorHandler);
reject(new Error(`Socket connection timeout after ${timeout}ms`));
}, timeout);
// Add event listeners
this.socketB.on("connect", connectHandler);
this.socketB.on("connect_error", errorHandler);
});
};
} }
handleConnectionFailure() { handleConnectionFailure() {
@@ -96,6 +158,10 @@ class SocketProvider extends Component {
console.log("SocketProvider: Disconnecting socket"); console.log("SocketProvider: Disconnecting socket");
this.socket.disconnect(); this.socket.disconnect();
} }
if (this.socketB) {
console.log("SocketProvider: Disconnecting socketB");
this.socketB.disconnect();
}
} }
render() { render() {
@@ -104,7 +170,7 @@ class SocketProvider extends Component {
window.__PRERENDER_FALLBACK__; window.__PRERENDER_FALLBACK__;
return ( return (
<SocketContext.Provider value={this.socket}> <SocketContext.Provider value={{socket:this.socket,socketB:this.socketB}}>
{/* Always render children but control visibility */} {/* Always render children but control visibility */}
<div style={{ display: this.state.connected ? 'block' : 'none' }}> <div style={{ display: this.state.connected ? 'block' : 'none' }}>
{this.props.children} {this.props.children}

View File

@@ -314,7 +314,11 @@ export default {
hot: true, hot: true,
port: 9500, port: 9500,
open: false, open: false,
historyApiFallback: true, historyApiFallback: {
index: '/index.html',
disableDotRule: true,
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
},
client: { client: {
logging: 'verbose', logging: 'verbose',
overlay: { overlay: {