From d33092e9bb4a0ef8def5bee65fac945b3b36e42e Mon Sep 17 00:00:00 2001 From: puneetnith28 Date: Sun, 11 Jan 2026 19:21:05 +0530 Subject: [PATCH] feat(onboarding): add user onboarding flow with monthly leaderboard focus - Created 5-step onboarding carousel with navigation controls - Added dedicated card for monthly leaderboard competition - Integrated onboarding into routing (shows for logged-out users) - Added storage utilities for onboarding state management - Included 'View Tutorial Again' option on welcome screen - Added 'View Tutorial' option in More page settings - Reset onboarding status on user logout - Onboarding content aligned with BrowsePing features and vision --- .../components/onboarding/OnboardingFlow.tsx | 192 ++++++++++++++++++ src/popup/components/onboarding/index.ts | 1 + .../components/welcome/WelcomeScreen.tsx | 21 +- src/popup/context/AuthContext.tsx | 3 +- src/popup/pages/MorePage.tsx | 16 +- src/popup/router/AppRouter.tsx | 18 +- src/popup/utils/localStorage.ts | 38 +++- 7 files changed, 273 insertions(+), 16 deletions(-) create mode 100644 src/popup/components/onboarding/OnboardingFlow.tsx create mode 100644 src/popup/components/onboarding/index.ts diff --git a/src/popup/components/onboarding/OnboardingFlow.tsx b/src/popup/components/onboarding/OnboardingFlow.tsx new file mode 100644 index 0000000..f42d2a6 --- /dev/null +++ b/src/popup/components/onboarding/OnboardingFlow.tsx @@ -0,0 +1,192 @@ +import React, { useState } from 'react'; +import { FiMonitor, FiShield, FiUsers, FiCheck, FiTrendingUp } from 'react-icons/fi'; +import { useNavigate } from 'react-router-dom'; + +interface OnboardingFlowProps { + onComplete?: () => void; +} + +const OnboardingFlow: React.FC = ({ onComplete }) => { + const [currentStep, setCurrentStep] = useState(0); + const navigate = useNavigate(); + + const steps = [ + { + icon: ( +
+ BrowsePing Logo +
+ ), + title: "Welcome to BrowsePing", + description: "Browsing doesn't have to be lonely. Connect with friends, share your online presence, and discover what's capturing everyone's attention across the web.", + features: [ + "See what friends are browsing", + "Share your activity in real-time", + "Discover content together" + ] + }, + { + icon: , + title: "Powerful Analytics & Insights", + description: "Understand your digital habits with detailed analytics about your browsing patterns, time spent online, and most-visited sites.", + features: [ + "Track your browsing time", + "Analyze tab usage patterns", + "View hourly activity insights" + ] + }, + { + icon: , + title: "Monthly Leaderboard Competition", + description: "Compete with friends on the monthly activity leaderboard. See who's the most active browser and climb to the top!", + features: [ + "Track monthly online activity", + "Compete with your network", + "Earn bragging rights" + ] + }, + { + icon: , + title: "Complete Privacy Control", + description: "You decide what to share and with whom. Granular privacy settings giengage in real-time conversations.", + features: [ + "Add and manage friends", + "Real-time messaging", + "Stay connected alwayyou want" + ] + }, + { + icon: , + title: "Connect & Engage", + description: "Build your social browsing network. Add friends, send messages, and compete on monthly activity leaderboards.", + features: [ + "Add and manage friends", + "Real-time messaging", + "Monthly leaderboards" + ] + }, + { + icon: , + title: "Ready to Get Started?", + description: "Join thousands making browsing more social. Create your free account now and start connecting with friends.", + features: [ + "Quick & easy signup", + "Free forever", + "Join your friends today" + ] + } + ]; + + const handleNext = async () => { + if (currentStep < steps.length - 1) { + setCurrentStep(currentStep + 1); + } else { + if (onComplete) { + onComplete(); + } else { + navigate('/welcome'); + } + } + }; + + const handleBack = () => { + if (currentStep > 0) { + setCurrentStep(currentStep - 1); + } + }; + + const handleSkip = async () => { + if (onComplete) { + onComplete(); + } else { + navigate('/welcome'); + } + }; + + const currentStepData = steps[currentStep]; + + return ( +
+
+
+
+ {currentStepData.icon} +
+ +
+

+ {currentStepData.title} +

+

+ {currentStepData.description} +

+
+ +
+ {currentStepData.features.map((feature, index) => ( +
+
+ {feature} +
+ ))} +
+
+
+ +
+
+ {steps.map((_, index) => ( +
+ ))} +
+ +
+ {currentStep > 0 && ( + + )} + + {currentStep < steps.length - 1 && ( + + )} + + +
+ +
+ Step {currentStep + 1} of {steps.length} +
+
+
+ ); +}; + +export default OnboardingFlow; diff --git a/src/popup/components/onboarding/index.ts b/src/popup/components/onboarding/index.ts new file mode 100644 index 0000000..17e3f57 --- /dev/null +++ b/src/popup/components/onboarding/index.ts @@ -0,0 +1 @@ +export { default as OnboardingFlow } from './OnboardingFlow'; diff --git a/src/popup/components/welcome/WelcomeScreen.tsx b/src/popup/components/welcome/WelcomeScreen.tsx index 7009fb4..62c9c34 100644 --- a/src/popup/components/welcome/WelcomeScreen.tsx +++ b/src/popup/components/welcome/WelcomeScreen.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { FiUserPlus, FiClock, FiShield } from 'react-icons/fi'; +import { FiUserPlus, FiClock, FiShield, FiCompass } from 'react-icons/fi'; const WelcomeScreen: React.FC = () => { return ( -
-
+
+
-
+
BrowsePing Logo {

Connect with friends across the web

-
+ -

+

Join BrowsePing to see what your friends are browsing in real-time, with full privacy controls.

-
+
diff --git a/src/popup/context/AuthContext.tsx b/src/popup/context/AuthContext.tsx index 45fe294..47d9a75 100644 --- a/src/popup/context/AuthContext.tsx +++ b/src/popup/context/AuthContext.tsx @@ -1,5 +1,5 @@ import React, { createContext, useState, useContext, useEffect } from 'react'; -import { User, updateUserInLocalStorage, getUserFromLocalStorage, clearUserFromLocalStorage } from '../utils/localStorage'; +import { User, updateUserInLocalStorage, getUserFromLocalStorage, clearUserFromLocalStorage, resetOnboardingStatus } from '../utils/localStorage'; import { logout as logoutAPI } from '../../services/api'; import toast from 'react-hot-toast'; @@ -67,6 +67,7 @@ export const AuthProvider: React.FC<{children: React.ReactNode}> = ({ children } const handleLogoutLocal = async () => { setUser(null); await clearUserFromLocalStorage(); + await resetOnboardingStatus(); chrome.runtime.sendMessage({ type: 'LOGOUT' }); }; diff --git a/src/popup/pages/MorePage.tsx b/src/popup/pages/MorePage.tsx index 18395e5..7e17994 100644 --- a/src/popup/pages/MorePage.tsx +++ b/src/popup/pages/MorePage.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import { FiUser, FiBell, FiHelpCircle, FiInfo, FiLogOut, FiShield, FiMail } from 'react-icons/fi'; +import { FiUser, FiBell, FiHelpCircle, FiInfo, FiLogOut, FiShield, FiMail, FiCompass } from 'react-icons/fi'; import { useAuth } from '../context/AuthContext'; import toast from 'react-hot-toast'; +import { resetOnboardingStatus } from '../utils/localStorage'; interface MenuItem { icon: React.ReactNode; @@ -20,6 +21,11 @@ const MorePage: React.FC = () => { window.location.href = '#/'; }; + const handleViewTutorial = async () => { + await resetOnboardingStatus(); + window.location.href = '#/onboarding'; + }; + const menuItems: MenuItem[] = [ // Account Section { @@ -37,7 +43,13 @@ const MorePage: React.FC = () => { category: 'account' }, - // Preferences Section + { + icon: , + label: 'View Tutorial', + description: 'Learn how to use BrowsePing features', + action: handleViewTutorial, + category: 'preferences' + }, { icon: , label: 'Notifications', diff --git a/src/popup/router/AppRouter.tsx b/src/popup/router/AppRouter.tsx index d0a9196..9bfd061 100644 --- a/src/popup/router/AppRouter.tsx +++ b/src/popup/router/AppRouter.tsx @@ -34,6 +34,8 @@ import AboutPage from '../pages/AboutPage'; import HelpPage from '../pages/HelpPage'; import PrivacyPage from '../pages/PrivacyPage'; import { MessageProvider } from '../context/MessageContext'; +import { OnboardingFlow } from '../components/onboarding'; +import { getOnboardingStatus } from '../utils/localStorage'; const AppRouter: React.FC = () => { const { user, loading } = useAuth(); @@ -50,16 +52,15 @@ const AppRouter: React.FC = () => { const forgotPasswordStep = localStorage.getItem('forgotPasswordStep'); const currentHash = window.location.hash; - // Handle email verification flow if (pendingEmail && (currentHash === '#/' || currentHash === '' || currentHash === '#')) { setTimeout(() => { if (window.location.hash === '#/' || window.location.hash === '' || window.location.hash === '#') { window.location.hash = '#/email-verification'; } }, 200); + return; } - // Handle forgot password flow restoration if (forgotPasswordStep && (currentHash === '#/' || currentHash === '' || currentHash === '#')) { setTimeout(() => { if (window.location.hash === '#/' || window.location.hash === '' || window.location.hash === '#') { @@ -76,6 +77,15 @@ const AppRouter: React.FC = () => { } } }, 200); + return; + } + + if (currentHash === '#/' || currentHash === '' || currentHash === '#') { + setTimeout(() => { + if (window.location.hash === '#/' || window.location.hash === '' || window.location.hash === '#') { + window.location.hash = '#/onboarding'; + } + }, 200); } } }, [user, loading]); @@ -94,7 +104,9 @@ const AppRouter: React.FC = () => { {/* Auth Routes */} {!user && ( <> - } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/src/popup/utils/localStorage.ts b/src/popup/utils/localStorage.ts index e6b7cfe..1f59c76 100644 --- a/src/popup/utils/localStorage.ts +++ b/src/popup/utils/localStorage.ts @@ -117,9 +117,6 @@ export const getUserFromLocalStorage = (): Promise => { }); }; -/** - * Clears user data from localStorage - */ export const clearUserFromLocalStorage = (): Promise => { return new Promise((resolve) => { chrome.storage.local.remove('user', () => { @@ -130,3 +127,38 @@ export const clearUserFromLocalStorage = (): Promise => { }); }); }; + +export const getOnboardingStatus = (): Promise => { + return new Promise((resolve) => { + chrome.storage.local.get('hasCompletedOnboarding', (result) => { + if (chrome.runtime.lastError) { + console.error('Error reading onboarding status:', chrome.runtime.lastError); + resolve(false); + return; + } + resolve(result.hasCompletedOnboarding || false); + }); + }); +}; + +export const setOnboardingComplete = (): Promise => { + return new Promise((resolve) => { + chrome.storage.local.set({ hasCompletedOnboarding: true }, () => { + if (chrome.runtime.lastError) { + console.error('Error setting onboarding status:', chrome.runtime.lastError); + } + resolve(); + }); + }); +}; + +export const resetOnboardingStatus = (): Promise => { + return new Promise((resolve) => { + chrome.storage.local.set({ hasCompletedOnboarding: false }, () => { + if (chrome.runtime.lastError) { + console.error('Error resetting onboarding status:', chrome.runtime.lastError); + } + resolve(); + }); + }); +};