Chapter 1: Application Views#
Welcome to the tutorial for the mapbox-gl_deck.gl_turf.js-ts
project! This first chapter is all about understanding a fundamental concept in building applications: Application Views. Don't worry if you're new to web development or these specific libraries; we'll take it step by step.
What's the Problem?#
Imagine you're building an app that helps people explore buildings using photos and maps. Sometimes the user needs to upload a photo. Other times, they need to see that photo's location and building details on a map. Maybe they also want to browse a gallery of all the photos they've uploaded.
You can't show everything on the screen at once! You need a way to switch between different screens or sections depending on what the user is doing. This is where the concept of "Application Views" comes in.
Think of your app like a building. Different "rooms" are designed for different activities:
- A Photo Upload Room where you handle taking or uploading pictures.
- A Map Room showing the location and details of a specific building related to a photo.
- A Gallery Room showcasing many photos on a map.
- A Login Room where users enter their credentials.
Application Views are simply these different "rooms" or distinct screens your application can display.
What are Application Views?#
In our project, Application Views represent the major sections or modes of the application. They control:
- What you see: The specific user interface elements displayed.
- What you can do: The interactions available to the user in that mode.
Switching between views is like walking from one room to another in our building analogy. The "main brain" of the application decides which room you should be in based on what you're trying to do (like uploading a photo or viewing the gallery).
How Views Work in This Project#
Our project uses React, a popular JavaScript library for building user interfaces. In React, we often represent different parts of the UI as components. Our "views" are essentially just different React components.
The key idea is that one main component is responsible for deciding which view component to show at any given time.
Let's look at how we define these different "rooms" in our project.
Defining the Rooms: The LAYOUT
Enum#
To keep track of which view (or room) we are currently in, we use a simple list of possibilities. In TypeScript, we can use something called an enum
(short for enumeration) for this. It's like giving a specific name to each possible view state.
Look at the src/types/layout.ts
file:
// src/types/layout.ts
export enum LAYOUT {
LOGIN,
PHOTO,
RESULT,
SHOWCASE,
}
This LAYOUT
enum defines our four main views:
LAYOUT.LOGIN
: The login screen.LAYOUT.PHOTO
: The screen for uploading or taking a photo.LAYOUT.RESULT
: The screen showing the map and details for a single photo upload result.LAYOUT.SHOWCASE
: The screen showing a gallery of photos on a map.
This enum gives us clear, easy-to-read names to represent the different states of our application's UI.
The Building Manager: MainView.tsx
#
Now, let's look at the "main brain" component that manages which view is currently displayed. This is handled by the MainView
component in src/views/main-view.tsx
.
MainView
keeps track of the current view using a piece of state. State is just data that a component can hold and manage, and when it changes, the component usually re-renders to update the display.
Here's a simplified look at how MainView
manages the current view using state:
// Inside src/views/main-view.tsx (simplified)
import React, { useState } from "react";
import { LAYOUT } from "../types/layout"; // Import our view definitions
// Import the components for each view
import LoginView from "./login-view";
import { PhotoView } from "./photo-view";
import { MapResultView } from "./map-result-view";
import { MapShowcaseView } from "./map-showcase-view";
export const MainView = () => {
// This piece of state tracks the currently active view
const [activeLayout, setActiveLayout] = useState(LAYOUT.LOGIN); // Start at the Login view
// ... other state variables and functions ...
// Function to change the active view
const onLayoutChange = (newLayout: number) => {
// Here you might add logic, e.g., check if geo data is available
if (geo || newLayout === LAYOUT.SHOWCASE || newLayout === LAYOUT.LOGIN) {
setActiveLayout(newLayout); // Update the state
} else {
alert("Please upload a photo first!");
setActiveLayout(LAYOUT.PHOTO); // Stay on or go back to Photo view
}
};
const handleLogin = () => {
setActiveLayout(LAYOUT.SHOWCASE); // Change view after successful login
};
return (
<Box sx={{ display: "flex", height: "100vh" }} ref={ref}>
{/* ... other global UI like CSS Baseline ... */}
{/* Conditional Rendering: Show the view based on activeLayout */}
{activeLayout === LAYOUT.LOGIN && (
<LoginView onClick={handleLogin} setBearerToken={setBearerToken} />
)}
{activeLayout === LAYOUT.PHOTO && (
<PhotoView
tags={tags}
previewImg={previewImg}
extractedDrawerOpen={extractedDrawerOpen}
onImageChange={(result) => setSelectedImg(result)}
onShowcaseClick={() => setActiveLayout(LAYOUT.SHOWCASE)}
setExtractedDrawerOpen={setExtractedDrawerOpen}
/>
)}
{activeLayout === LAYOUT.RESULT && (
<MapResultView
geo={geo}
view={view}
imageUrl={previewImg}
drawLaz={drawLaz}
lazFile={lazFile}
onLazChange={onLazChangeHandler}
drawLaz_={drawLazHandler}
tags={tags}
previewImg={previewImg}
onImageChange={(result) => setSelectedImg(result)}
onShowcaseClick={() => setActiveLayout(LAYOUT.SHOWCASE)}
setExtractedDrawerOpen={setExtractedDrawerOpen}
extractedDrawerOpen={extractedDrawerOpen}
/>
)}
{activeLayout === LAYOUT.SHOWCASE && (
<MapShowcaseView
view={view}
geo={geo}
drawLaz_={drawLazHandler}
lazFile={lazFile}
onLazChange={onLazChangeHandler}
tags={tags}
previewImg={previewImg}
onImageChange={(result) => setSelectedImg(result)}
setGeo={setGeo}
onShowcaseClick={() => setActiveLayout(LAYOUT.SHOWCASE)}
setExtractedDrawerOpen={setExtractedDrawerOpen}
extractedDrawerOpen={extractedDrawerOpen}
drawLaz={drawLaz}
bearerToken={bearerToken}
/>
)}
{/* ... other global UI like BottomNav, Snackbar, Backdrop, Modal ... */}
</Box>
);
};
This code snippet shows the core idea:
MainView
usesuseState
to hold theactiveLayout
, initialized toLAYOUT.LOGIN
.- The
return
part uses conditional rendering (activeLayout === LAYOUT.LOGIN && <LoginView ... />
). This means: "IfactiveLayout
isLAYOUT.LOGIN
, then render theLoginView
component." - Only one of these conditions will be true at a time, so only one view component is rendered and displayed.
- Functions like
onLayoutChange
andhandleLogin
are defined inMainView
. When these functions are called (usually triggered by user interaction inside one of the view components, passed down as props), they callsetActiveLayout
. - Calling
setActiveLayout
updates the state, React sees the state has changed, and re-rendersMainView
, showing the component that now matches the newactiveLayout
value.
The Flow of Switching Views#
Let's trace a simple path: logging in and seeing the showcase gallery.
sequenceDiagram
participant User
participant LoginView
participant MainView
participant MapShowcaseView
User->LoginView: Enters credentials and Clicks "ENTER"
LoginView->MainView: Calls onClick prop (handleLogin function)
MainView->MainView: Updates activeLayout state to LAYOUT.SHOWCASE
MainView->MapShowcaseView: Renders MapShowcaseView component
MapShowcaseView-->>User: Displays the Gallery/Map UI
Note over User, MapShowcaseView: User is now in the Showcase view
- The application starts,
MainView
renders, and its initial stateactiveLayout
isLAYOUT.LOGIN
. - The conditional rendering in
MainView
shows theLoginView
component. - The user interacts with
LoginView
(enters details, clicks "ENTER"). - The "ENTER" button click in
LoginView
calls theonClick
function that was passed down as a prop fromMainView
. This function inMainView
ishandleLogin
. handleLogin
callssetActiveLayout(LAYOUT.SHOWCASE)
.MainView
's state changes, triggering a re-render.- During the re-render,
MainView
checks the conditions again.activeLayout === LAYOUT.LOGIN
is now false, butactiveLayout === LAYOUT.SHOWCASE
is true. MainView
stops renderingLoginView
and starts renderingMapShowcaseView
.- The user now sees the UI defined by
MapShowcaseView
.
The MainView
component acts like a central controller, managing the overall application flow by switching between these different view components based on actions.
Conclusion#
In this chapter, we learned that Application Views are the distinct screens or modes of our application. We saw how the LAYOUT
enum helps us define these views and how the MainView
component uses state and conditional rendering to decide which view component to display at any given time. This structure helps organize our application and separate different functionalities into manageable parts.
Next, we'll dive into how the application handles user actions, specifically focusing on file uploads and other interactions that trigger changes in our application, including switching between the views we just discussed.
Next Chapter: User Input Handling (Files & Interaction)
Generated by AI Codebase Knowledge Builder. References: 1(https://github.com/buildvoc/mapbox-gl_deck.gl_turf.js-ts/blob/3d8a4a53d878db3324af6466e0f99e5fb072bbe7/src/types/layout.ts), 2(https://github.com/buildvoc/mapbox-gl_deck.gl_turf.js-ts/blob/3d8a4a53d878db3324af6466e0f99e5fb072bbe7/src/views/login-view.tsx), 3(https://github.com/buildvoc/mapbox-gl_deck.gl_turf.js-ts/blob/3d8a4a53d878db3324af6466e0f99e5fb072bbe7/src/views/main-view.tsx), 4(https://github.com/buildvoc/mapbox-gl_deck.gl_turf.js-ts/blob/3d8a4a53d878db3324af6466e0f99e5fb072bbe7/src/views/map-result-view.tsx), 5(https://github.com/buildvoc/mapbox-gl_deck.gl_turf.js-ts/blob/3d8a4a53d878db3324af6466e0f99e5fb072bbe7/src/views/map-showcase-view.tsx), 6(https://github.com/buildvoc/mapbox-gl_deck.gl_turf.js-ts/blob/3d8a4a53d878db3324af6466e0f99e5fb072bbe7/src/views/photo-view.tsx)