-
Notifications
You must be signed in to change notification settings - Fork 43
[PUB-1135] Add LiveObjects examples (LiveMap and LiveCounter) #2437
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
Open
VeskeR
wants to merge
8
commits into
main
Choose a base branch
from
PUB-1135/liveobjects-example
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
1a4dd42
Add LiveObjects LiveCounter example
VeskeR ac6777e
Add LiveObjects LiveMap example
VeskeR 0fe244b
Update liveobjects examples to use npm workspace and franken-ui compo…
GregHolmes 7670ad9
Update liveobjects example to use latest LiveObjects changes from abl…
VeskeR 51354dd
Rename file to resolve NPM caching issues
GregHolmes b01ae14
Update LiveObjects examples to use new npm workspace implementations
GregHolmes 9afc875
Update to latest ably-js LiveObjects build
VeskeR dd9121a
Update README and TypeScript files for LiveObjects examples
VeskeR File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.yarn/install-state.gz | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env*.local | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# Synchronizing numeric values with LiveCounter | ||
|
||
Enable clients to update and synchronize numerical values in an application in realtime. | ||
|
||
LiveCounter is a synchronized numerical counter that supports increment and decrement operations. It ensures that all updates are correctly applied and synchronized across users in realtime, preventing inconsistencies when multiple users modify the counter value simultaneously. | ||
|
||
LiveCounter is useful for tracking values that need to be updated dynamically, such as votes, reaction counts, live leaderboards, game stats, or other numeric data points. | ||
|
||
LiveCounter is implemented using [Ably LiveObjects](/docs/liveobjects). LiveObjects, as a feature of [Ably Pub/Sub](/docs/channels), contains a set of purpose-built APIs that abstract away the complexities of managing shared state between clients in an application. It is built on top of Ably's core platform, and so it provides the same performance guarantees and scaling potential. | ||
|
||
## Resources | ||
|
||
Use the following methods to interact with a LiveCounter in your application: | ||
|
||
- [`objects.getRoot()`](https://ably.com/docs/sdk/js/v2.0/interfaces/ably.Objects.html#getRoot): retrieves the root object that serves as the starting point for storing and organizing objects on a channel. | ||
- [`objects.createCounter()`](https://ably.com/docs/sdk/js/v2.0/interfaces/ably.Objects.html#createCounter): creates a new LiveCounter instance. | ||
- [`liveCounter.value()`](https://ably.com/docs/sdk/js/v2.0/interfaces/ably.LiveCounter.html#value): returns the current value of a counter. | ||
- [`liveCounter.increment()`](https://ably.com/docs/sdk/js/v2.0/interfaces/ably.LiveCounter.html#increment): sends the operation message to the Ably system to increase the counter value. | ||
- [`liveCounter.decrement()`](https://ably.com/docs/sdk/js/v2.0/interfaces/ably.LiveCounter.html#decrement): sends the operation message to the Ably system to decrease the counter value. | ||
- [`liveCounter.subscribe()`](https://ably.com/docs/sdk/js/v2.0/interfaces/ably.LiveCounter.html#subscribe): subscribes to LiveCounter updates by registering a listener. | ||
|
||
Find out more about [LiveCounter](/docs/liveobjects/counter). | ||
|
||
## Getting started | ||
|
||
1. Clone the [Ably docs](https://github.com/ably/docs) repository where this example can be found: | ||
|
||
```sh | ||
git clone [email protected]:ably/docs.git | ||
``` | ||
|
||
2. Change directory: | ||
|
||
```sh | ||
cd /examples/ | ||
``` | ||
|
||
3. Rename the environment file: | ||
|
||
```sh | ||
mv .env.example .env.local | ||
``` | ||
|
||
4. In `.env.local` update the value of `VITE_ABLY_KEY` to be your Ably API key. | ||
|
||
5. Install dependencies: | ||
|
||
```sh | ||
yarn install | ||
``` | ||
|
||
6. Run the server: | ||
|
||
```sh | ||
yarn run liveobjects-live-counter-javascript | ||
``` | ||
|
||
7. Try it out by opening two tabs to [http://localhost:5173/](http://localhost:5173/) with your browser to see the result. | ||
|
||
## Open in CodeSandbox | ||
|
||
In CodeSandbox, rename the `.env.example` file to `.env.local` and update the value of your `VITE_ABLY_KEY` variable to use your Ably API key. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet" /> | ||
<link rel="stylesheet" href="src/styles.css" /> | ||
<title>LiveCounter example</title> | ||
</head> | ||
<body class="font-inter"> | ||
<div class="min-h-screen p-8"> | ||
<div class="max-w-sm mx-auto bg-white rounded-lg shadow-lg p-6 space-y-2"> | ||
<h2 class="text-xl font-bold mb-4">Vote for your favorite Color</h2> | ||
|
||
<div id="vote-options"> | ||
<div class="flex justify-between items-center p-2 border-b space-x-4"> | ||
<span class="text-red-500 font-semibold flex-grow">Red</span> | ||
<span class="font-bold text-gray-700" id="count-red">0</span> | ||
<button | ||
class="uk-btn uk-btn-primary uk-border-rounded-right whitespace-nowrap vote-button" | ||
data-color="red" | ||
> | ||
Vote | ||
</button> | ||
</div> | ||
|
||
<div class="flex justify-between items-center p-2 border-b space-x-4"> | ||
<span class="text-green-500 font-semibold flex-grow">Green</span> | ||
<span class="font-bold text-gray-700" id="count-green">0</span> | ||
<button | ||
class="uk-btn uk-btn-primary uk-border-rounded-right whitespace-nowrap vote-button" | ||
data-color="green" | ||
> | ||
Vote | ||
</button> | ||
</div> | ||
|
||
<div class="flex justify-between items-center p-2 space-x-4"> | ||
<span class="text-blue-500 font-semibold flex-grow">Blue</span> | ||
<span class="font-bold text-gray-700" id="count-blue">0</span> | ||
<button | ||
class="uk-btn uk-btn-primary uk-border-rounded-right whitespace-nowrap vote-button" | ||
data-color="blue" | ||
> | ||
Vote | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<button class="uk-btn uk-btn-secondary uk-border-rounded-right" id="reset">Reset</button> | ||
</div> | ||
</div> | ||
<script type="module" src="src/script.ts"></script> | ||
</body> | ||
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"name": "liveobjects-live-counter-javascript", | ||
"version": "1.0.0", | ||
"main": "index.js", | ||
"license": "MIT", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc && vite build", | ||
"preview": "vite preview" | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
examples/liveobjects-live-counter/javascript/src/ably.config.d.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { LiveCounter } from 'ably'; | ||
import { Color } from './script'; | ||
|
||
declare global { | ||
export interface ObjectsTypes { | ||
root: { | ||
[Color.red]: LiveCounter; | ||
[Color.green]: LiveCounter; | ||
[Color.blue]: LiveCounter; | ||
}; | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
examples/liveobjects-live-counter/javascript/src/script.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { DefaultRoot, LiveCounter, LiveMap, Realtime } from 'ably'; | ||
import Objects from 'ably/objects'; | ||
import { nanoid } from 'nanoid'; | ||
import './styles.css'; | ||
|
||
export enum Color { | ||
red = 'red', | ||
green = 'green', | ||
blue = 'blue', | ||
} | ||
|
||
const client = new Realtime({ | ||
clientId: nanoid(), | ||
key: import.meta.env.VITE_ABLY_KEY as string, | ||
environment: 'sandbox', | ||
plugins: { Objects }, | ||
}); | ||
|
||
const urlParams = new URLSearchParams(window.location.search); | ||
|
||
const channelName = urlParams.get('name') || 'objects-live-counter'; | ||
const channel = client.channels.get(channelName, { modes: ['OBJECT_PUBLISH', 'OBJECT_SUBSCRIBE'] }); | ||
|
||
const colorCountDivs: Record<Color, HTMLElement> = { | ||
red: document.getElementById('count-red')!, | ||
green: document.getElementById('count-green')!, | ||
blue: document.getElementById('count-blue')!, | ||
}; | ||
const countersReset = document.getElementById('reset')!; | ||
|
||
async function main() { | ||
await channel.attach(); | ||
|
||
const objects = channel.objects; | ||
const root = await objects.getRoot(); | ||
|
||
await initCounters(root); | ||
addEventListenersToButtons(root); | ||
} | ||
|
||
async function initCounters(root: LiveMap<DefaultRoot>) { | ||
// subscribe to root to get notified when counter objects get changed on the root. | ||
// for example, when we reset all counters | ||
root.subscribe(({ update }) => { | ||
Object.entries(update).forEach(([keyName, change]) => { | ||
if (change === 'removed') { | ||
return; | ||
} | ||
|
||
if (Object.values(Color).includes(keyName as Color)) { | ||
// key pointing to a counter object got updated, resubscribe to a counter | ||
const color = keyName as Color; | ||
subscribeToCounterUpdates(color, root.get(color)!); | ||
} | ||
}); | ||
}); | ||
|
||
await Promise.all( | ||
Object.values(Color).map(async (color) => { | ||
if (root.get(color)) { | ||
subscribeToCounterUpdates(color, root.get(color)!); | ||
return; | ||
} | ||
|
||
await root.set(color, await channel.objects.createCounter()); | ||
}), | ||
); | ||
} | ||
|
||
function subscribeToCounterUpdates(color: Color, counter: LiveCounter) { | ||
counter.subscribe(() => { | ||
colorCountDivs[color].innerHTML = counter.value().toString(); | ||
}); | ||
colorCountDivs[color].innerHTML = counter.value().toString(); | ||
} | ||
Comment on lines
+70
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add cleanup for counter subscriptions. The subscriptions to counter updates should be cleaned up to prevent memory leaks. function subscribeToCounterUpdates(color: Color, counter: LiveCounter) {
- counter.subscribe(() => {
+ const subscription = counter.subscribe(() => {
colorCountDivs[color].innerHTML = counter.value().toString();
});
colorCountDivs[color].innerHTML = counter.value().toString();
+ return subscription; // Return subscription for cleanup
} And update the calling code to handle cleanup: // Store subscriptions for cleanup
const subscriptions = new Map();
// In initCounters:
const subscription = subscribeToCounterUpdates(color, counter);
subscriptions.set(color, subscription);
// Add cleanup function:
function cleanup() {
subscriptions.forEach(subscription => subscription.unsubscribe());
subscriptions.clear();
} |
||
|
||
function addEventListenersToButtons(root: LiveMap<DefaultRoot>) { | ||
document.querySelectorAll('.vote-button').forEach((button) => { | ||
const color = button.getAttribute('data-color') as Color; | ||
button.addEventListener('click', () => { | ||
root.get(color)?.increment(1); | ||
}); | ||
}); | ||
|
||
countersReset.addEventListener('click', () => { | ||
Object.values(Color).forEach(async (color) => root.set(color, await channel.objects.createCounter())); | ||
}); | ||
} | ||
|
||
main() | ||
.then() | ||
.catch((e) => console.error(e)); | ||
VeskeR marked this conversation as resolved.
Show resolved
Hide resolved
|
60 changes: 60 additions & 0 deletions
60
examples/liveobjects-live-counter/javascript/src/styles.css
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
@tailwind base; | ||
@tailwind components; | ||
@tailwind utilities; | ||
|
||
@layer base { | ||
:root { | ||
--background: 0 0% 100%; | ||
--foreground: 20 14.3% 4.1%; | ||
--card: 0 0% 100%; | ||
--card-foreground: 20 14.3% 4.1%; | ||
--popover: 0 0% 100%; | ||
--popover-foreground: 20 14.3% 4.1%; | ||
--primary: 20 91% 54%; | ||
--primary-foreground: 60 9.1% 97.8%; | ||
--secondary: 209 100% 43%; | ||
--secondary-foreground: 60 9.1% 97.8%; | ||
--muted: 60 4.8% 95.9%; | ||
--muted-foreground: 25 5.3% 44.7%; | ||
--accent: 60 4.8% 95.9%; | ||
--accent-foreground: 24 9.8% 10%; | ||
--destructive: 0 84.2% 60.2%; | ||
--destructive-foreground: 60 9.1% 97.8%; | ||
--border: 20 5.9% 90%; | ||
--input: 20 5.9% 90%; | ||
--ring: 24.6 95% 53.1%; | ||
--radius: 0.5rem; | ||
--chart-1: 12 76% 61%; | ||
--chart-2: 173 58% 39%; | ||
--chart-3: 197 37% 24%; | ||
--chart-4: 43 74% 66%; | ||
--chart-5: 27 87% 67%; | ||
} | ||
|
||
.dark { | ||
--background: 20 14.3% 4.1%; | ||
--foreground: 60 9.1% 97.8%; | ||
--card: 20 14.3% 4.1%; | ||
--card-foreground: 60 9.1% 97.8%; | ||
--popover: 20 14.3% 4.1%; | ||
--popover-foreground: 60 9.1% 97.8%; | ||
--primary: 20 91% 54%; | ||
--primary-foreground: 60 9.1% 97.8%; | ||
--secondary: 209 100% 43%; | ||
--secondary-foreground: 60 9.1% 97.8%; | ||
--muted: 12 6.5% 15.1%; | ||
--muted-foreground: 24 5.4% 63.9%; | ||
--accent: 12 6.5% 15.1%; | ||
--accent-foreground: 60 9.1% 97.8%; | ||
--destructive: 0 72.2% 50.6%; | ||
--destructive-foreground: 60 9.1% 97.8%; | ||
--border: 12 6.5% 15.1%; | ||
--input: 12 6.5% 15.1%; | ||
--ring: 20.5 90.2% 48.2%; | ||
--chart-1: 220 70% 50%; | ||
--chart-2: 160 60% 45%; | ||
--chart-3: 30 80% 55%; | ||
--chart-4: 280 65% 60%; | ||
--chart-5: 340 75% 55%; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
examples/liveobjects-live-counter/javascript/tailwind.config.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import baseConfig from '../../tailwind.config'; | ||
import type { Config } from 'tailwindcss'; | ||
|
||
const config: Config = { | ||
...baseConfig, | ||
content: ['./src/**/*.{js,ts,tsx}', './index.html'], | ||
}; | ||
|
||
export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"include": ["./src/**/*.ts", "./src/**/*.tsx"], | ||
"exclude": ["../../node_modules", "../../dist", "../../lib"] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
interface ImportMetaEnv { | ||
readonly VITE_ABLY_KEY: string; | ||
// Add other environment variables here if needed | ||
} | ||
|
||
interface ImportMeta { | ||
readonly env: ImportMetaEnv; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { defineConfig } from 'vite'; | ||
import baseConfig from '../../vite.config'; | ||
|
||
export default defineConfig({ | ||
...baseConfig, | ||
envDir: '../../', | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ably-js liveobjects is not published on npm yet, so local build of ably-js on liveobjects branch is provided to be able to test the examples
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should avoid merging into main until ably-js is up to date to cover the LiveObjects work. Do we have any idea when this is expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, will wait.
npm release will most likely be mid next week