This article walks through the creation process of a generic Progressive Web Application (PWA) template. The goal was to build a reusable foundation packed with common features needed for modern web apps, including offline capabilities, internationalization, common UI components, and robust cloud synchronization using the Dropbox API.
The full code for this template is available on GitHub here.
The application uses two main HTML files: index.html
for the main application interface and help/index.html
for a separate help page. Both leverage Bootstrap 5 for layout and core components.
index.html
sets up the primary view:
#offcanvasSidebar
) used for file management (listing files, add/rename/delete buttons).#currentFileNameHeader
), a language switcher dropdown, the Dropbox sync status indicator (#syncStatusIndicator
), the Dropbox connection button (#dropboxAuthButton
), and a link to the help page.#main-content-area
) where application-specific UI elements can be added, pre-filled with examples of the template’s UI components (datepickers, switches, notification buttons).#addFileModal
), renaming files (#renameFileModal
), confirming deletions (#deleteFileModalConfirm
), and resolving sync conflicts (#conflictModal
).
<!-- Snippet from index.html Header -->
<div class="pt-3 pb-2 mb-3 border-bottom d-flex justify-content-between align-items-center flex-wrap">
<div class="d-flex align-items-center me-3 mb-2 mb-md-0">
<button class="btn btn-light me-3" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasSidebar">...</button>
<h1 class="h2 mb-0" id="currentFileNameHeader" data-i18n="main.header.currentFile">Current File</h1>
</div>
<div class="d-flex align-items-center">
<!-- Language Dropdown -->
<div class="dropdown me-2">...</div>
<!-- Sync Status -->
<span id="syncStatusIndicator" class="me-2 text-muted small"></span>
<!-- Dropbox Button -->
<button type="button" id="dropboxAuthButton" class="btn btn-light p-1 btn-fa me-2">...</button>
<!-- Help Link -->
<a href="help/" class="btn btn-light btn-fa p-1">...</a>
</div>
</div>
The structure is designed to be clean and adaptable for various PWA applications built upon this template.
Styling relies heavily on Bootstrap 5 for the base framework. Customizations and component-specific styles are added via:
TEMPLATE.css
: Contains general overrides, theme colors (primary #0083B3
and complementary #FF4030
), button styling, layout adjustments (like responsive button rounding), and styles for the file list sidebar and Dropbox button. It also includes a simple mechanism to prevent Flash of Unstyled Content (FOUC) during language initialization.assets/css/ui/
, assets/css/notif.min.css
): Minified CSS files provide specific styling for the datepicker, switch component, and notification toasts, ensuring they integrate visually with the theme.
/* Example Theme Variables in TEMPLATE.css */
.btn-primary {
--bs-btn-color:#FFF;
--bs-btn-bg:#0083B3;
--bs-btn-border-color:#0083B3;
/* ... hover, active, disabled states ... */
}
.btn-complementary {
--bs-btn-color: #FFF;
--bs-btn-bg: #FF4030;
--bs-btn-border-color: #FF4030;
/* ... hover, active, disabled states ... */
}
/* FOUC Prevention */
body {
opacity: 0; /* Initially hidden */
transition: opacity 0.4s ease-in-out;
}
body.localized {
opacity: 1; /* Visible once i18next is ready */
}
The styling aims for a clean, modern look that can be easily customized further.
The application logic is modularized, promoting separation of concerns and maintainability. Key modules include:
This script acts as the main entry point for UI interactions after the document is ready.
initializeDropboxSync()
to start the Dropbox connection process and sets up listeners for the file management buttons (Add, Rename, Delete).setupAddFileModalListeners
, etc.) the first time a modal is opened.Handles data persistence and file management logic.
storage.js
: Provides low-level access to localStorage
. It manages:
knownFiles
key) storing {name, path}
objects.activeFile
key).content_/path/to/file.txt
).DEFAULT_FILE_PATH
(/default.txt
) always exists in the known files list.localDataChanged
event when content is saved locally, used by the sync coordinator.files.js
: Implements higher-level file operations triggered by UI interactions (sidebar clicks, modal submissions).
updateFileSelectionUI
).localStorage
), updates the UI, and then attempts to rename the file on Dropbox via the API module.This is a core feature, enabling synchronization of files stored in the app’s dedicated Dropbox folder.
dropbox-sync.js
: The main entry point for the sync system. It initializes authentication and offline handling. It doesn’t contain the core sync logic itself but orchestrates the initialization.dropbox/config.js
: Stores essential configuration like the Dropbox App Key (CLIENT_ID
), calculates the REDIRECT_URI
dynamically, and defines localStorage
keys used by the sync system. Note: The CLIENT_ID
needs to be replaced with an actual App Key from Dropbox.dropbox/auth.js
: Manages the OAuth 2.0 authentication flow using the Dropbox.DropboxAuth
SDK component. It handles obtaining, storing (localStorage
), and clearing the access token, processing the redirect from Dropbox, and initializing the API client (initializeDropboxApi
) upon success. It also includes logic (discoverDropboxFiles
) to list files in the app’s Dropbox folder upon login and add any newly found .txt
files to the local known files list.dropbox/api.js
: Contains functions that interact directly with the Dropbox API using the Dropbox.Dropbox
SDK client (dbx
). It provides wrappers for:
dbx
instance.filesGetMetadata
).filesDownload
).filesUpload
), ensuring overwrite
mode.filesMoveV2
).filesDeleteV2
).dropbox/ui.js
: Manages UI elements related to Dropbox:
#syncStatusIndicator
) with different icons/text based on the state (Idle, Syncing, Pending, Offline, Error, Not Connected).#conflictModal
), using a Promise to return the user’s choice (‘local’ or ‘dropbox’).#dropboxAuthButton
).dropbox/offline.js
: Handles network connectivity changes. It listens for browser online
and offline
events. If the app comes online and an upload was flagged as pending (because a local change happened while offline), it triggers a sync attempt. It uses localStorage
to track pending uploads per file.sync-coordinator.js
: Orchestrates the actual synchronization logic.
localDataChanged
event dispatched by storage.js
.coordinateSync
.coordinateSync
: This is the core comparison logic. It fetches local and Dropbox timestamps for the active file.
dropbox/ui.js
.dropbox/api.js
for downloads/uploads and dropbox/ui.js
for status updates and conflict resolution.These scripts initialize and manage the reusable UI components.
datepicker.js
: Initializes the Bootstrap Datepicker component on elements with the .date-picker
class, allowing theme customization via data-datepicker-color
.switch.js
: Initializes Bootstrap switches, setting their initial text label (e.g., “ON”/“OFF”) based on state and updating the label on change. It reads custom labels from data-checked
/data-unchecked
attributes.notif.js
: Provides functions (showPrimaryNotification
, showComplementaryNotification
) to trigger example notifications using the showNotification
function. It also contains the actual logic to initialize switches and set their labels using i18next
for translation, falling back to data attributes or defaults. (Note: There seems to be duplicate switch initialization logic between switch.js
and notif.js
. notif.js
’s version is more complete as it includes i18n).notif-flash.min.js
: A utility to display notifications based on URL query parameters (e.g., after a redirect). It defines the global showNotification
function used by other modules to create styled Bootstrap Toasts for user feedback.Implements multi-language support using i18next
and its plugins.
i18next
with the browser language detector and HTTP backend to load translation files (assets/locales/{lang}/translation.json
).data-i18n
attributes and updates their text content or specified attributes (e.g., [title]key
) based on the loaded translations for the current language.i18next
language, and updates the UI accordingly. Manages the display of the current language in the header dropdown.Provides offline functionality and basic PWA features.
service-worker.js
: (Code not shown in blob, but assumed standard) Implements caching strategies (e.g., cache-first for assets) to allow the app to load and function offline. Handles installation, activation (cache cleanup), and fetch events. Likely includes a mechanism to receive messages (like ‘refresh_cache’).cache.js
: Fetches a version file (/data/json/version.json
) from the server, compares it to a version stored in localStorage
, and if different, sends a refresh_cache
message to the service worker to trigger an update of the cached assets.A simple utility module providing logVerbose
and warnVerbose
functions that only output to the console if a VERBOSE_LOGGING_ENABLED
flag is set to true
, useful for debugging during development.
This PWA template provides a solid starting point for web applications requiring offline support, file management, and cloud synchronization via Dropbox. By modularizing the JavaScript and leveraging libraries like Bootstrap and i18next, it offers a flexible and feature-rich foundation. The Dropbox integration, with its attention to conflict resolution and offline handling, adds significant value for applications needing data persistence across devices.