Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example app for WASM / Web Sync #5880

Merged
merged 55 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
cca765b
Set up and configure React app.
elle-j Jun 1, 2023
db34fb1
Add 'Task' model.
elle-j Jun 1, 2023
5d2b0d9
Add 'TaskItem' component.
elle-j Jun 1, 2023
f9b98e0
Add 'TaskList' component.
elle-j Jun 1, 2023
62a00d2
Add 'AddTaskForm' component.
elle-j Jun 1, 2023
5658064
Add 'IntroText' component.
elle-j Jun 1, 2023
a64902f
Add 'useTaskManager' hook.
elle-j Jun 1, 2023
484b6d6
Add 'TaskScreen' screen/page.
elle-j Jun 1, 2023
bbada76
Update 'App' component w/ 'RealmProvider' and 'TaskScreen'.
elle-j Jun 1, 2023
a87c47d
Remove 'source-map-support'.
elle-j Jun 5, 2023
f87423c
Add 'realm' and 'realm/react' to deps.
elle-j Jun 5, 2023
9aa2779
Add example app to workspaces.
elle-j Jun 5, 2023
7eda710
Update userId prop schema.
elle-j Jun 5, 2023
d5b6cb9
Add 'useAppManager' hook.
elle-j Jun 5, 2023
73fba87
Add sync and router.
elle-j Jun 5, 2023
8557991
Add form to 'LoginScreen'.
elle-j Jun 5, 2023
58e6054
Update Atlas App ID.
elle-j Jun 5, 2023
663f6a5
Update router.
elle-j Jun 5, 2023
a61c66a
Refactor routing.
elle-j Jun 6, 2023
736c521
Add styles to login page.
elle-j Jun 7, 2023
8616436
Remove explicit React imports.
elle-j Jun 7, 2023
a54f291
Add styles to tasks page.
elle-j Jun 8, 2023
5a23eb2
Fix rerendering issue.
elle-j Jun 8, 2023
01ed826
Minor updates.
elle-j Jun 8, 2023
4761e78
Memoize 'TaskItem'.
elle-j Jun 8, 2023
bc744dd
Move assets to own directory.
elle-j Jun 9, 2023
81765a1
Remove 'test' script.
elle-j Jun 9, 2023
3c76de9
Remove memoization of 'TaskItem'.
elle-j Jun 9, 2023
bde28a0
Update README.
elle-j Jun 9, 2023
8821c14
Update README.
elle-j Jun 9, 2023
41f9159
Rename App components.
elle-j Jun 10, 2023
a23684b
Add project structure to README.
elle-j Jun 10, 2023
b3c57da
Specify how to modify project configs in README.
elle-j Jun 10, 2023
997ca25
Update style module name.
elle-j Jun 12, 2023
411a240
Refactor form reset logic to separate function.
elle-j Jun 12, 2023
34e3a23
Replace App ID with placeholder.
elle-j Jun 15, 2023
c85bd7c
Skip resetting login form after successful login.
elle-j Jun 15, 2023
fc8f2d6
Delegate auth logic to 'UserProvider' via 'fallback' prop.
elle-j Jun 15, 2023
7c41fb5
Move dev dependencies to 'devDependencies'.
elle-j Jun 15, 2023
46c8a91
Remove 'eject' script.
elle-j Jun 15, 2023
f2e4aa5
Move app into 'examples' directory.
elle-j Jun 15, 2023
03ef549
Add 'Limitations' to README.
elle-j Jun 15, 2023
63ca93b
Show login/registration error in UI.
elle-j Jun 15, 2023
892ae6a
Remove debugging logs.
elle-j Jun 16, 2023
2531b09
Remove React logo.
elle-j Jun 16, 2023
9dd7cad
Update 'ErrorPage'.
elle-j Jun 16, 2023
984865c
Add Realm copyright headers.
elle-j Jun 16, 2023
9a1e14c
Update Realm imports.
elle-j Jun 16, 2023
c2f5b7f
Replace source deps. w/ realm and realm/react packages.
elle-j Jun 20, 2023
fd1084c
Replace custom hook w/ realm/react hook.
elle-j Jun 20, 2023
795db5b
Add scrolling to task description.
elle-j Jun 21, 2023
80ebcfe
Remove package lock file.
elle-j Jun 21, 2023
90fd2f5
Update 'Limitations' in README.
elle-j Jun 21, 2023
d98ddd6
Use latest release of realm/react.
elle-j Jun 21, 2023
d3a5495
Rename directory to 'example-react-task'.
elle-j Jun 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions examples/example-react-task/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
148 changes: 148 additions & 0 deletions examples/example-react-task/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Example React App Using Realm & Sync for Web

This is an example React Todo/Task app for showcasing Realm and Sync for Web.

## MongoDB & Realm Functionality

### Use cases

* Log in and register (email/password)
* Log out
* Create tasks
* Read/query tasks
* Update the status of tasks
* Delete tasks
* Sync
1. Tasks are stored locally in an in-memory realm..
2. then synced to MongoDB Atlas..
3. then synced to all other apps connected to the same Atlas App.
* Local/Offline-first
* All CRUD functionality works while offline.
* Realm JS and [@realm/react](https://www.npmjs.com/package/@realm/react) hooks

### Screenshot

![Tasks Page](./src/assets/screenshot-realm-web-sync-tasks.png)

## Project Structure

The following shows the project structure and the most relevant files.

```
├── public
│ └── index.html - File served to client
├── src
│ ├── atlas-app-services
│ │ └── config.json - Set Atlas App ID
│ │
│ ├── components
│ │ ├── AddTaskForm.tsx - Trigger create task
│ │ ├── NavBar.tsx - Trigger logout
│ │ ├── TaskItem.tsx - Trigger update/delete task
│ │ └── TaskList.tsx - Render all tasks
│ │
│ ├── hooks
│ │ └── useTaskManager.ts - Handle CRUD task
│ │
│ ├── models
│ │ └── Task.ts - Data model
│ │
│ ├── pages
│ │ ├── LoginPage.tsx - Trigger login/register
│ │ └── TaskPage.tsx - Pass CRUD ops to children
│ │
│ ├── App.tsx - Get and provide Atlas App
│ ├── AuthenticatedApp.tsx - Open and provide Realm & User
│ └── index.tsx - Entry point
├── craco.config.ts - Configure CRA
├── package.json - Specify Node dependencies
└── tsconfig.json - Configure TypeScript
```

## Getting Started

### Prerequisites

* [Node.js](https://nodejs.org/en) v16 or later

### Installation

Clone the repository and the current branch, then navigate to the example app folder:

```sh
cd realm-js/examples/example-react-task
```

Install dependencies:

```sh
npm install
```

### Setting up an Atlas App and Device Sync

To sync data you must first:

1. [Create an App Services App](https://www.mongodb.com/docs/atlas/app-services/manage-apps/create/create-with-ui/)
2. [Enable Email/Password Authentication](https://www.mongodb.com/docs/atlas/app-services/authentication/email-password/#std-label-email-password-authentication)
* For development purposes, you can also automatically confirm users:
* In the App Services UI, go to the **Authentication** tab > **Authentication Providers** > Edit **Email/Password** > **User Confirmation Method**
3. [Enable Flexible Sync](https://www.mongodb.com/docs/atlas/app-services/sync/configure/enable-sync/) with **Development Mode** on.
* When Development Mode is enabled, queryable fields will be added automatically.
* Queryable fields used in this app: `_id`, `isComplete`
4. Select a **global** [deployment region](https://www.mongodb.com/docs/atlas/app-services/apps/deployment-models-and-regions/#deployment-models---regions):
* In the App Services UI, go to the **App Settings** tab > **General** > **Deployment Region**
5. Allow client requests from all or specific IP addresses:
* In the App Services UI, go to the **App Settings** tab > **IP Access List** > **Add IP Address**
6. [Set read/write permissions](https://www.mongodb.com/docs/atlas/app-services/rules/roles/#with-device-sync) for the collection.
* This app assumes all users can read and write all tasks in the collection.
* In the App Services UI, go to the **Rules** tab > Click on the **Task** collection > Add a `readAndWriteAll` role.
* *You may need to run the client before seeing the **Task** collection.*

Once done, [copy your App ID](https://www.mongodb.com/docs/atlas/app-services/reference/find-your-project-or-app-id/#std-label-find-your-app-id) from the App Services UI and paste it as the value of `ATLAS_APP_ID` in [src/atlas-app-services/config.json](./src/atlas-app-services/config.json):

```json
{
"ATLAS_APP_ID": "YOUR_ID"
}
```

### Building the App

Navigate to `example-react-task/` and build the app (the output will be located in the `build/` folder and is minified):

```sh
npm run build
```

### Running the App

Navigate to `example-react-task/` and start the app in development mode:

```sh
npm start
```

This should automatically open your default browser; but if not, open [http://localhost:3000](http://localhost:3000).

### Troubleshooting

A great way to troubleshoot sync-related errors is to read the [logs in the App Services UI](https://www.mongodb.com/docs/atlas/app-services/logs/logs-ui/).

## Limitations

### Persistence & WebWorkers

In the current state of the Realm JS SDK for browsers, in-memory realms are used for temporarily storing the data locally. A realm's data will be lost as soon as its last instance is closed.

It is not possible to share a realm instance across contexts such as browser tabs or WebWorker instances. Therefore, a hard refresh in the browser will clear the local data (e.g. logged in users will need to reauthenticate).

Note that the data will still be persisted in MongoDB Atlas and synced to the client once authenticated.

This app uses client-side routing to retain the local data across different routes.

### Encryption

Since realms are memory-only and not locally persisted, it is an error to provide an encryption key in the realm configuration. Sync traffic remains encrypted with HTTPS as usual.
37 changes: 37 additions & 0 deletions examples/example-react-task/craco.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2023 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

import { CracoConfig } from "@craco/types";

const config: CracoConfig = {
webpack: {
configure(config, context) {
config.experiments = {
topLevelAwait: true,
...config.experiments,
};

return config;
},
},
devServer: {
open: "chrome",
},
};

export default config;
50 changes: 50 additions & 0 deletions examples/example-react-task/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@realm/example-react-task",
"version": "0.1.0",
"description": "Example React App Using Realm & Sync for Web",
"private": true,
"scripts": {
"start": "craco start",
"build": "craco build"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"dependencies": {
"@realm/react": "^0.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.2",
"realm": "12.0.0-browser.2",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@craco/craco": "^7.1.0",
"@craco/types": "^7.1.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.34",
"@types/react": "^18.2.7",
"@types/react-dom": "^18.2.4",
"react-scripts": "5.0.1",
"typescript": "^4.9.5"
}
}
Binary file added examples/example-react-task/public/favicon.ico
Binary file not shown.
42 changes: 42 additions & 0 deletions examples/example-react-task/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>MongoDB Realm, WASM, and Sync App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
15 changes: 15 additions & 0 deletions examples/example-react-task/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "Realm WASM",
"name": "MongoDB Realm and WASM App",
"icons": [
{
"src": "favicon.ico",
"sizes": "32x32 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#7e41f8",
"background_color": "#ebebeb"
}
3 changes: 3 additions & 0 deletions examples/example-react-task/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
56 changes: 56 additions & 0 deletions examples/example-react-task/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2023 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import { AppProvider } from '@realm/react';

import { AuthenticatedApp } from './AuthenticatedApp';
import { ErrorPage } from './pages/ErrorPage';
import { LoginPage } from './pages/LoginPage';
import { TaskPage } from './pages/TaskPage';
import config from './atlas-app-services/config.json';
import styles from './styles/App.module.css';

const router = createBrowserRouter([
{
path: '/',
element: <LoginPage />,
errorElement: <ErrorPage />,
},
{
element: <AuthenticatedApp />,
children: [
{
path: 'tasks',
element: <TaskPage />
}
]
}
]);

function App() {
return (
<div className={styles.container}>
<AppProvider id={config.ATLAS_APP_ID}>
<RouterProvider router={router} />
</AppProvider>
</div>
);
}

export default App;
Loading