Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

61 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

WorldLoop

Discover the world, one country at a time.

A TikTok-style mobile app for exploring countries through immersive feeds, interactive maps, AI-generated insights, and culture videos.

ExpoReact NativeTypeScriptNode.jsRedis


Overview

WorldLoop turns every country into a swipeable content unit — like a short-form video feed, but for geography and culture. Users browse portrait hero imagery, watch travel clips, spin a 3D globe, and dive into AI-powered country profiles.

The project is built as a full-stack learning showcase: a polished Expo mobile client paired with a TypeScript API that handles caching, media sourcing, and AI generation server-side.


Features

ScreenWhat it does
OnboardingWelcome flow with hero imagery and a clear path into the app
ExploreVertical, full-screen country feed with hero images, stats, and AI fun facts
AI ExplorerDeep-dive country profile with stats, map focus, and Wikipedia context
MapInteractive globe with country pins, spatial discovery, and tap-to-preview
CultureTikTok-style vertical video feed sourced from travel & culture clips per country
SavedBookmark countries on a constellation-style saved space
SearchGlobal search overlay with region filters across feeds and map

Highlights

  • TikTok-style UX — vertical paging feeds with snap transitions and immersive media
  • Smart image pipeline — Unsplash → Pexels → Wikipedia fallback, cached per country in Redis
  • Video culture feed — portrait-first travel clips from Pexels / Pixabay
  • Spatial discovery — viewport-based country ranking on the map via bounding-box queries
  • AI content layer — fun facts, captions, and explorer summaries generated server-side
  • Responsive imagery — backend serves size-appropriate image variants based on device pixel width

Screenshots

Device captures from the Expo app (assets/screenshots/), in screen order:

Onboarding
WorldLoop onboarding welcome screen
Explore
Explore feed showing Estonia
AI Explorer
AI Explorer country profile for Ecuador
Map
Interactive map focused on Africa
Culture
Culture video feed for San Marino
Saved
Saved countries constellation view

Architecture

flowchart LR
subgraph client["Expo App"]
UI["Screens & Components"]
Store["Zustand Stores"]
UI --> Store
end
subgraph api["Backend API"]
Routes["Express Routes"]
Services["Country · Feed · Image · Video · AI"]
Cache["Redis Cache"]
Routes --> Services --> Cache
end
subgraph external["External APIs"]
RC["REST Countries"]
IMG["Unsplash / Pexels"]
VID["Pexels / Pixabay"]
AI["OpenAI"]
end
Store -->|"REST"| Routes
Services --> RC
Services --> IMG
Services --> VID
Services --> AI
Loading

Design principles

  • API keys and AI calls stay on the backend — never in the mobile client
  • Country data is normalized into a single content model consumed by every screen
  • Redis caches expensive upstream calls (images, videos, AI) with long TTLs
  • Zustand + AsyncStorage handle client state and persistence

Tech Stack

Mobile (Expo)

LayerTools
FrameworkExpo SDK 54, React Native, Expo Router
LanguageTypeScript
StylingNativeWind v5, Tailwind CSS v4
StateZustand, AsyncStorage
Mediaexpo-image, expo-video, Lottie
Maps & 3Dreact-native-maps, Three.js, @react-three/fiber
Animationreact-native-reanimated, react-native-gesture-handler

Backend

LayerTools
RuntimeNode.js, Express 5, TypeScript
CacheRedis 7 (Docker)
DataREST Countries API
ImagesUnsplash, Pexels, Wikipedia
VideoPexels Video, Pixabay
AIOpenAI (gpt-4o-mini)

Getting Started

Prerequisites

  • Node.js 20+
  • Docker Desktop (recommended, for Redis)
  • iOS Simulator, Android Emulator, or Expo Go

1. Clone & install

git clone https://github.com/<birthtand>/worldloop.git
cd worldloop
npm install

2. Start Redis

docker compose up -d redis
docker compose exec redis redis-cli ping # should return PONG

3. Configure environment

# Mobile app
cp .env.example .env
# Backend APIcd backend
cp .env.example .env
npm install

Add API keys in backend/.env as needed (images, video, and AI features degrade gracefully without them):

UNSPLASH_ACCESS_KEY=PEXELS_API_KEY=PIXABAY_API_KEY=OPENAI_API_KEY=

4. Run the backend

cd backend
npm run dev
# → http://localhost:3001

Confirm the startup log shows Redis connected.

5. Run the app

# from repo root
npx expo start

Press i for iOS Simulator, a for Android Emulator, or scan the QR code with Expo Go.


Project Structure

worldloop/
├── app/ # Expo Router screens (tabs, onboarding, country routes)
├── assets/
│ └── screenshots/ # README & portfolio device captures (s1–s6)
├── components/ # Reusable UI (explore, culture, map, ai-explorer, …)
├── store/ # Zustand stores (feeds, map, search, saved)
├── lib/ # API client, map math, formatting helpers
├── hooks/ # Data-fetching and screen logic hooks
├── constants/ # Themes, images, regions, map styles
├── backend/ # Node.js + Express API
│ └── src/
│ ├── api/ # Route definitions
│ ├── controllers/ # Request handlers
│ ├── services/ # Business logic (feed, image, video, AI, cache)
│ └── lib/ # Validation, upstream helpers
├── prompts-worldloop/ # Incremental backend build prompts
└── docker-compose.yml # Local Redis

API Overview

MethodEndpointDescription
GET/healthHealth check
GET/country/:nameSingle country with images
GET/feed/countriesPaginated explore feed
GET/feed/culture/countriesVideo-only culture feed
GET/map/countriesAll countries with coordinates
GET/discoverSpatial bbox country discovery

Full backend docs: backend/README.md


Scripts

Mobile

npm start # Expo dev server
npm run android # Open on Android
npm run ios # Open on iOS
npm run lint # ESLint
npm run typecheck # TypeScript check
npm test# Vitest

Backend

cd backend
npm run dev # Watch mode (tsx)
npm run build # Compile to dist/
npm run typecheck # TypeScript check

What I Built

This project demonstrates end-to-end mobile product development:

  • Feed engineering — cursor-based pagination, prefetching, and region filters
  • Map & spatial UX — 3D globe rendering, clustering, viewport discovery
  • Media orchestration — multi-provider fallbacks with Redis-backed caching
  • API design — typed services, validation, rate limiting, and secure key handling
  • Mobile polish — custom tab bar, glass UI, onboarding, and immersive full-screen layouts

License

This project is for portfolio and educational purposes. External API content (images, videos) is subject to each provider's terms of use.


Built with Expo · React Native · TypeScript

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages