-
Notifications
You must be signed in to change notification settings - Fork 42
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
[PUB-1135] Add LiveObjects examples (LiveMap and LiveCounter) #2437
base: main
Are you sure you want to change the base?
Conversation
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis pull request introduces two new examples—LiveCounter and LiveMap—that utilize Ably’s real-time messaging capabilities. Both examples have configuration enhancements such as new environment variables for Ably, standardized Git ignore files, TypeScript configuration, and Vite/Tailwind CSS integrations. New HTML, script, and documentation files have been added to provide interactive, real-time functionality for voting counters and task management across multiple clients. Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant UI as Browser UI
participant LC as LiveCounter App
participant A as Ably Service
U->>UI: Clicks vote button
UI->>LC: Triggers counter update
LC->>A: Publishes counter increment
A-->>LC: Broadcasts updated counter
LC->>UI: Updates counter display
sequenceDiagram
participant U as User
participant UI as Task Board UI
participant LM as LiveMap App
participant A as Ably Service
U->>UI: Enters new task
UI->>LM: Sends add task event
LM->>A: Updates LiveMap with new task data
A-->>LM: Broadcasts task update
LM->>UI: Refreshes task list display
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
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.
Actionable comments posted: 11
🔭 Outside diff range comments (1)
examples/liveobjects-live-map/javascript/src/script.ts (1)
112-132
: 🛠️ Refactor suggestionAdd input validation for task title.
The task addition should include proper input validation to ensure data quality.
function addEventListenersToButtons(root: LiveMap<DefaultRoot>) { addTaskButton.addEventListener('click', async () => { const taskTitle = taskInput.value.trim(); - if (!taskTitle) { + if (!taskTitle || taskTitle.length < 3) { + alert('Task title must be at least 3 characters long'); return; } const taskId = nanoid(); taskInput.value = ''; - await root.get('tasks').set(taskId, taskTitle); + try { + await root.get('tasks').set(taskId, taskTitle); + } catch (error) { + console.error('Failed to add task:', error); + alert('Failed to add task. Please try again.'); + } });
🧹 Nitpick comments (8)
examples/liveobjects-live-counter/javascript/src/ably.config.d.ts (1)
4-12
: Consider a more maintainable type definition.While the current implementation works, consider making it more maintainable by using a generic type instead of hardcoding enum values:
declare global { export interface LiveObjectsTypes { root: { - [Color.red]: LiveCounter; - [Color.green]: LiveCounter; - [Color.blue]: LiveCounter; + [K in Color]: LiveCounter; }; } }This approach:
- Reduces duplication
- Automatically updates when new colors are added to the enum
- Maintains type safety
examples/liveobjects-live-counter/javascript/index.html (2)
15-39
: Enhance accessibility and user experience.The voting interface could benefit from improved accessibility and loading states.
<div id="vote-options"> + <div role="region" aria-live="polite" id="vote-status" class="sr-only"></div> <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="bg-blue-500 hover:bg-blue-600 text-white py-1 px-3 rounded vote-button" data-color="red"> + <button class="bg-blue-500 hover:bg-blue-600 text-white py-1 px-3 rounded vote-button" + data-color="red" + aria-label="Vote for red" + disabled> + <span class="vote-text">Vote</span> + <span class="loading-text hidden">Voting...</span> </button> </div> <!-- Similar changes for green and blue buttons --> </div>Add corresponding JavaScript to handle loading states:
function handleVoteClick(button: HTMLButtonElement) { button.disabled = true; button.querySelector('.vote-text')?.classList.add('hidden'); button.querySelector('.loading-text')?.classList.remove('hidden'); // After vote completes: button.disabled = false; button.querySelector('.vote-text')?.classList.remove('hidden'); button.querySelector('.loading-text')?.classList.add('hidden'); }
41-41
: Add confirmation for reset action.The reset button should have a confirmation step to prevent accidental resets.
-<button class="bg-blue-500 hover:bg-blue-600 text-white py-1 px-3 rounded" id="reset">Reset</button> +<button + class="bg-blue-500 hover:bg-blue-600 text-white py-1 px-3 rounded" + id="reset" + aria-label="Reset all votes" + onclick="return confirm('Are you sure you want to reset all votes?')"> + Reset +</button>examples/liveobjects-live-counter/page.md (1)
70-70
: Minor Style Improvement on CodeSandbox Instructions
The sentence in the CodeSandbox instructions that says “update the value of yourVITE_PUBLIC_ABLY_KEY
variable to use your Ably API key” contains redundant wording as flagged by the static analysis tool. A small rephrase could improve readability.🧰 Tools
🪛 LanguageTool
[style] ~70-~70: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...E_PUBLIC_ABLY_KEY` variable to use your Ably API key.(ADVERB_REPETITION_PREMIUM)
examples/liveobjects-live-map/page.md (2)
1-4
: Clarify the Opening Description
The opening lines for LiveMap provide essential context, but the phrasing “update and synchronize key/value data across in an application in realtime” could be improved for clarity. Consider rephrasing to something like:“Enable clients to update and synchronize key/value pairs in real time throughout an application.”
70-70
: Minor Style Suggestion on CodeSandbox Instructions
The sentence in the CodeSandbox instructions regarding updating the environment variable might benefit from a slight rephrasing to avoid repetition as highlighted by the static analysis tool.🧰 Tools
🪛 LanguageTool
[style] ~70-~70: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...E_PUBLIC_ABLY_KEY` variable to use your Ably API key.(ADVERB_REPETITION_PREMIUM)
examples/liveobjects-live-map/javascript/index.html (2)
6-7
: Optimize font loading performance.Consider adding
font-display: swap
to the font loading strategy to improve perceived performance.- <link href="https://fonts.googleapis.com/css?family=Inter" rel="stylesheet" /> + <link href="https://fonts.googleapis.com/css?family=Inter&display=swap" rel="stylesheet" />
31-33
: Add loading and empty states.The tasks container should include loading and empty states to improve user experience.
<div class="h-full border rounded-lg overflow-y-auto bg-white shadow-lg"> - <div id="tasks" class="p-4 space-y-4"></div> + <div id="tasks" class="p-4 space-y-4"> + <div id="loading-state" class="text-center py-4 text-gray-500">Loading tasks...</div> + <div id="empty-state" class="text-center py-4 text-gray-500 hidden">No tasks yet. Add your first task above!</div> + </div> </div>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
examples/liveobjects-live-counter/javascript/yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
examples/liveobjects-live-map/javascript/yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (26)
examples/liveobjects-live-counter/javascript/.env.example
(1 hunks)examples/liveobjects-live-counter/javascript/.gitignore
(1 hunks)examples/liveobjects-live-counter/javascript/README.md
(1 hunks)examples/liveobjects-live-counter/javascript/index.html
(1 hunks)examples/liveobjects-live-counter/javascript/package.json
(1 hunks)examples/liveobjects-live-counter/javascript/src/ably.config.d.ts
(1 hunks)examples/liveobjects-live-counter/javascript/src/script.ts
(1 hunks)examples/liveobjects-live-counter/javascript/src/styles.css
(1 hunks)examples/liveobjects-live-counter/javascript/tailwind.config.js
(1 hunks)examples/liveobjects-live-counter/javascript/tsconfig.json
(1 hunks)examples/liveobjects-live-counter/javascript/vite-env.d.ts
(1 hunks)examples/liveobjects-live-counter/javascript/vite.config.ts
(1 hunks)examples/liveobjects-live-counter/page.md
(1 hunks)examples/liveobjects-live-map/javascript/.env.example
(1 hunks)examples/liveobjects-live-map/javascript/.gitignore
(1 hunks)examples/liveobjects-live-map/javascript/README.md
(1 hunks)examples/liveobjects-live-map/javascript/index.html
(1 hunks)examples/liveobjects-live-map/javascript/package.json
(1 hunks)examples/liveobjects-live-map/javascript/src/ably.config.d.ts
(1 hunks)examples/liveobjects-live-map/javascript/src/script.ts
(1 hunks)examples/liveobjects-live-map/javascript/src/styles.css
(1 hunks)examples/liveobjects-live-map/javascript/tailwind.config.js
(1 hunks)examples/liveobjects-live-map/javascript/tsconfig.json
(1 hunks)examples/liveobjects-live-map/javascript/vite-env.d.ts
(1 hunks)examples/liveobjects-live-map/javascript/vite.config.ts
(1 hunks)examples/liveobjects-live-map/page.md
(1 hunks)
✅ Files skipped from review due to trivial changes (16)
- examples/liveobjects-live-map/javascript/tailwind.config.js
- examples/liveobjects-live-map/javascript/src/styles.css
- examples/liveobjects-live-counter/javascript/src/styles.css
- examples/liveobjects-live-map/javascript/.env.example
- examples/liveobjects-live-counter/javascript/.env.example
- examples/liveobjects-live-map/javascript/vite.config.ts
- examples/liveobjects-live-counter/javascript/tailwind.config.js
- examples/liveobjects-live-counter/javascript/README.md
- examples/liveobjects-live-counter/javascript/vite.config.ts
- examples/liveobjects-live-map/javascript/.gitignore
- examples/liveobjects-live-counter/javascript/.gitignore
- examples/liveobjects-live-map/javascript/tsconfig.json
- examples/liveobjects-live-counter/javascript/package.json
- examples/liveobjects-live-counter/javascript/tsconfig.json
- examples/liveobjects-live-map/javascript/README.md
- examples/liveobjects-live-map/javascript/package.json
🧰 Additional context used
🪛 LanguageTool
examples/liveobjects-live-counter/page.md
[style] ~70-~70: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...E_PUBLIC_ABLY_KEY` variable to use your Ably API key.
(ADVERB_REPETITION_PREMIUM)
examples/liveobjects-live-map/page.md
[style] ~70-~70: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ...E_PUBLIC_ABLY_KEY` variable to use your Ably API key.
(ADVERB_REPETITION_PREMIUM)
🔇 Additional comments (12)
examples/liveobjects-live-counter/javascript/vite-env.d.ts (2)
6-8
: LGTM! Well-structured type definitions.The implementation correctly follows Vite's TypeScript configuration pattern and ensures type safety through proper use of readonly modifiers.
1-4
:❓ Verification inconclusive
Verify the security implications of using VITE_PUBLIC_ABLY_KEY.
The "PUBLIC" prefix suggests this key will be exposed to the client-side code. Please ensure this is a publish-only API key with appropriate capability restrictions to prevent unauthorized access.
🌐 Web query:
What are the security best practices for Ably API keys in client-side applications?
💡 Result:
When securing Ably API keys in client-side applications, Token Authentication is the primary recommended approach to avoid exposing sensitive credentials. Here are the key best practices:
Use Token Authentication Instead of Basic Authentication
- Avoid Basic Authentication (direct use of API keys) in client-side code[4][6]. Basic authentication exposes your full API key string, risking compromise if intercepted.
- Generate short-lived tokens server-side using Ably's Token Request mechanism[4][6][9]. These tokens:
- Expire after a set time (default 60 minutes)
- Can be scope-limited to specific channels/operations
- Are cryptographically signed to prevent tampering
- Don't expose your master API key secrets
Server-Side Implementation Requirements
- Protect token generation endpoints
Authenticate users via solutions like Clerk[3] or OAuth before issuing tokens.- Use strong API keys
Generate keys with:
- 128+ character length[1]
- Mixed case letters, numbers, and symbols[1]
- Rotate API keys regularly
Automate rotation cycles (e.g., every 90 days)[1][5]Client-Side Security Measures
Safe Practices Risks to Avoid Authentication Token Requests[4][6] Hardcoded API keys[5] Storage Ephemeral token storage LocalStorage/SessionStorage[5] Transmission HTTPS only[1][4] Unencrypted HTTP Additional Best Practices
- Apply least privilege: Restrict token capabilities to minimum required operations[1][4]
- Monitor usage: Track anomalous token activity patterns[1][4]
- Automate secret detection: Use tools like Legit Security to scan for accidental exposures[1]
- Educate teams: Enforce policies against committing secrets to version control[1][5]
By combining Ably's token authentication system with standard API security practices, you maintain functionality while significantly reducing attack surfaces in client applications[4][6][9].
Citations:
- 1: https://www.legitsecurity.com/blog/api-key-security-best-practices
- 2: https://konghq.com/blog/learning-center/what-are-api-keys
- 3: https://dev.to/ably/how-to-authenticate-a-nextjs-route-handler-using-clerk-36pm
- 4: https://faqs.ably.com/token-and-basic-authentication
- 5: https://www.strac.io/blog/sharing-and-storing-api-keys-securely
- 6: https://ably.com/blog/ably-token-request-api-endpoints-in-vite-and-react
- 7: https://pkg.go.dev/github.com/ably/ably-go/ably
- 8: https://devforum.zoom.us/t/safe-to-use-api-key-in-client-side-code/59412
- 9: https://github.com/snakemode/ably-client-side-api-calls
- 10: https://devcenter.heroku.com/articles/ably
Security Notice: Client-Side Ably API Key Handling
The current use of
VITE_PUBLIC_ABLY_KEY
exposes an API key directly in client-side code. Ably's best practices advise against using full API keys on the client; instead, Token Authentication should be implemented. Please ensure that:
- The API key is strictly publishable (i.e., it is not a full-access key) and is limited to non-sensitive operations.
- You consider transitioning to a Token Authentication strategy where short-lived, scope-limited tokens are generated server-side.
- Any token generation endpoints are properly secured to avoid exposing sensitive credentials.
examples/liveobjects-live-map/javascript/src/ably.config.d.ts (1)
1-11
: LGTM! Well-structured type definitions.The type definitions are clear, properly scoped, and follow TypeScript best practices. The
Tasks
type andLiveObjectsTypes
interface provide good type safety for the LiveMap implementation.examples/liveobjects-live-counter/javascript/src/script.ts (1)
12-17
: Consider making the client configuration more flexible.The client configuration is hardcoded with
environment: 'sandbox'
. This might cause issues when deploying to production.Consider making the environment configurable through environment variables:
const client = new Realtime({ clientId: nanoid(), key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, - environment: 'sandbox', + environment: import.meta.env.VITE_ABLY_ENVIRONMENT || 'production', plugins: { LiveObjects }, });examples/liveobjects-live-counter/page.md (2)
1-9
: Strong Introduction and Overview
The introduction clearly explains the purpose and use cases of LiveCounter. It provides a solid context for developers.
11-26
: Comprehensive API Methods Documentation
The “Resources” section lists the key methods along with documentation links. This is valuable for users to quickly understand how to interact with LiveCounter. Ensure that once the final product documentation is available, these links are updated accordingly.examples/liveobjects-live-map/page.md (2)
5-9
: Well-Outlined LiveMap Features
This section offers a detailed overview of LiveMap’s functionality and potential use cases. It effectively communicates the benefits of using LiveMap for managing shared key/value state.
11-26
: Detailed API Reference for LiveMap
The “Resources” section is thorough and mirrors the clarity found in the LiveCounter documentation. The methods are clearly listed with relevant links. Remember to verify these links when final documentation becomes available.examples/liveobjects-live-map/javascript/vite-env.d.ts (2)
6-8
: LGTM! Well-structured type definitions.The interface correctly extends the global ImportMeta type, providing type-safe access to environment variables while preventing accidental modifications through the readonly modifier.
1-4
:❓ Verification inconclusive
Verify the security implications of exposing the Ably key.
The environment variable name includes "PUBLIC", suggesting this key will be exposed to the client side. Please ensure this is an Ably public/publishable key and not a private key.
Let me help you verify this by checking the Ably documentation:
🌐 Web query:
What is the difference between Ably public and private keys? Are there security implications of exposing Ably keys in client-side code?
💡 Result:
Ably's public and private keys serve distinct roles in authentication and encryption, with critical security implications if mishandled. Here's a breakdown:
Key Differences
Private API Keys
Used for server-side authentication with Ably services[9]. These grant full account access and should never be exposed client-side[4][9].
- Enable capabilities like channel management and message publishing
- Required for generating time-limited tokens[9]
Public-Facing Tokens
Temporary credentials derived from private keys via token authentication[9]:
- Expire after a set time (TTL)
- Contain granular permissions (e.g., specific channel access)[9]
- Safe for client-side use when properly configured
Security Risks of Exposing Keys
Exposing Ably private keys in client code creates multiple vulnerabilities[3][4][7]:
Immediate Threats
- Full account takeover via API key compromise[3]
- Unauthorized data access to all channels/messages[7]
- Financial abuse through API quota exhaustion[3][7]
Compliance Impacts
- GDPR/HIPAA violations from data breaches[2][9]
- Loss of SOC 2 Type 2 compliance status[2]
Operational Risks
- Service disruption through DDoS attacks[2][7]
- Message integrity compromise via payload tampering[4][7]
Mitigation Strategies
Ably recommends these security practices[2][4][9]:
Never embed private keys in client code
Use server-side token issuance instead[9]. Example secure flow:// Server-side token generation var capability = new Capability(); capability.AddResource("channel:chat", Capability.AllowAll); var tokenParams = new TokenParams { Ttl = TimeSpan.FromHours(1) }; var token = await rest.Auth.RequestTokenAsync(tokenParams, capability);Enable end-to-end encryption
Add AES encryption to sensitive channels[4]:var secretKey = Crypto.GetRandomKey(); var encryptedChannel = realtime.Get("secure-chat", new ChannelOptions(secretKey));Implement security controls:
- IP whitelisting for API endpoints[2]
- Rate limiting per API key/token[2][9]
- Regular key rotation schedules[7][9]
For maximum security, Ably's architecture ensures even their engineers cannot decrypt properly configured end-to-end encrypted messages without your private key[2][4]. Always follow principle of least privilege when assigning API key capabilities[9].
Citations:
- 1: https://www.preveil.com/blog/public-and-private-key/
- 2: https://ably.com/security-and-compliance
- 3: https://www.legitsecurity.com/blog/api-key-security-best-practices
- 4: https://github.com/ably/ably-dotnet
- 5: https://www.scalingparrots.com/en/public-key-vs-private-key-difference/
- 6: https://www.kiteworks.com/secure-file-sharing/public-vs-private-key-encryption/
- 7: https://www.finemountainconsulting.com/2023/09/26/the-dangers-of-exposing-api-keys-in-knack-apps-javascript-code-and-how-to-safeguard-your-apps/
- 8: https://venafi.com/blog/what-difference-between-public-key-and-private-key/
- 9: https://ably.com/docs/key-concepts
- 10: https://eprint.iacr.org/2024/394.pdf
Security Check: Validate Ably Key Exposure
Based on Ably’s documentation, only public (publishable) tokens are safe for client-side exposure. The environment variable name (
VITE_PUBLIC_ABLY_KEY
) correctly follows Vite’s naming convention, but please verify that it indeed holds a public token rather than a private API key. Using a private key client-side can lead to severe security risks, including unauthorized access and data breaches.Action Points:
- Confirm that
VITE_PUBLIC_ABLY_KEY
is set to a public Ably key (i.e., a token with restricted permissions) as intended.- If the key is private, switch to a secure, server-side token generation flow to mitigate exposure risks.
examples/liveobjects-live-map/javascript/src/script.ts (2)
7-12
: Remove hardcoded sandbox environment.As mentioned in the PR objectives, the sandbox environment configuration should be removed once the channel state feature is available in production.
const client = new Realtime({ clientId: nanoid(), key: import.meta.env.VITE_PUBLIC_ABLY_KEY as string, - environment: 'sandbox', plugins: { LiveObjects }, });
16-17
: LGTM! Channel setup looks good.The channel is correctly configured with appropriate modes for state publishing and subscribing.
function subscribeToCounterUpdates(color: Color, counter: LiveCounter) { | ||
counter.subscribe(() => { | ||
colorCountDivs[color].innerHTML = counter.value().toString(); | ||
}); | ||
colorCountDivs[color].innerHTML = counter.value().toString(); | ||
} |
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.
🛠️ 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 subscribeToTasksUpdates(tasks: Tasks) { | ||
tasksDiv.innerHTML = ''; | ||
|
||
tasks.subscribe(({ update }) => { | ||
Object.entries(update).forEach(async ([taskId, change]) => { | ||
switch (change) { | ||
case 'updated': | ||
tasksOnUpdated(taskId, tasks); | ||
break; | ||
case 'removed': | ||
tasksOnRemoved(taskId); | ||
break; | ||
} | ||
}); | ||
}); | ||
|
||
for (const [taskId] of tasks.entries()) { | ||
createTaskDiv({ id: taskId, title: tasks.get(taskId) }, tasks); | ||
} | ||
} |
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.
🛠️ Refactor suggestion
Add cleanup for task subscriptions.
The subscription should be cleaned up when no longer needed to prevent memory leaks and unexpected behavior.
function subscribeToTasksUpdates(tasks: Tasks) {
tasksDiv.innerHTML = '';
- tasks.subscribe(({ update }) => {
+ const subscription = tasks.subscribe(({ update }) => {
Object.entries(update).forEach(async ([taskId, change]) => {
switch (change) {
case 'updated':
tasksOnUpdated(taskId, tasks);
break;
case 'removed':
tasksOnRemoved(taskId);
break;
}
});
});
for (const [taskId] of tasks.entries()) {
createTaskDiv({ id: taskId, title: tasks.get(taskId) }, tasks);
}
+
+ return () => subscription.unsubscribe();
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
function subscribeToTasksUpdates(tasks: Tasks) { | |
tasksDiv.innerHTML = ''; | |
tasks.subscribe(({ update }) => { | |
Object.entries(update).forEach(async ([taskId, change]) => { | |
switch (change) { | |
case 'updated': | |
tasksOnUpdated(taskId, tasks); | |
break; | |
case 'removed': | |
tasksOnRemoved(taskId); | |
break; | |
} | |
}); | |
}); | |
for (const [taskId] of tasks.entries()) { | |
createTaskDiv({ id: taskId, title: tasks.get(taskId) }, tasks); | |
} | |
} | |
function subscribeToTasksUpdates(tasks: Tasks) { | |
tasksDiv.innerHTML = ''; | |
const subscription = tasks.subscribe(({ update }) => { | |
Object.entries(update).forEach(async ([taskId, change]) => { | |
switch (change) { | |
case 'updated': | |
tasksOnUpdated(taskId, tasks); | |
break; | |
case 'removed': | |
tasksOnRemoved(taskId); | |
break; | |
} | |
}); | |
}); | |
for (const [taskId] of tasks.entries()) { | |
createTaskDiv({ id: taskId, title: tasks.get(taskId) }, tasks); | |
} | |
return () => subscription.unsubscribe(); | |
} |
25be0fb
to
09e9bd1
Compare
09e9bd1
to
9f4cfeb
Compare
@GregHolmes I haven't added the React examples as we don't have the react hooks for liveobjects ready and I didn't want to spend time on preparing an example with React and plain liveobjects js which soon will need to be rewritten anyway. Does the new examples structure allow for React variant to be missing? Or it has to be present? |
9f4cfeb
to
9f40624
Compare
…y-js Local ably-js-liveobjects build is based on the next PR [1] [1] ably/ably-js#1985
7db22da
to
2ca853f
Compare
Description
Adds two code examples for the new LiveObjects feature of pub/sub SDK; for LiveMap and LiveCounter. It contains a TypeScript example showcasing a simple voting application powered by a LiveCounter, and a task list powered by a LiveMap.
Review
PR is currently placed in draft as there are still a couple of things to sort out before we can merge it into
main
:enableChannelState
feature flag, and the realtime clients in the example app are configured to usesandbox
environment. it should be removed once channel state is available in production.The examples themselves are ready for review in terms of structure, documentation, and API usage.
How to get the sandobx API key for the app with
enableChannelState
flagThe easiest way is probably to clone the ably-js repo locally and checkout the
integration/liveobjects
branch (https://github.com/ably/ably-js/tree/integration/liveobjects). Then launch tests with theABLY_LOG_LEVEL
environment set to4
:ABLY_LOG_LEVEL=4 npm run test
. Wait a couple of seconds until the app is created and one of the tests creates a new Client with an API key (10-15 seconds should be enough). Look forAbly: BaseClient(): initialized with clientOptions
log in the console then, it will have a JSON output of options used to create a client. Copy thekey
value from those options - this is the API key you can use to run the examples in this repo.How to launch
You may get
Integrity check failed for "ably"
error when doingyarn install
due to ably being installed from the locally provided tgz file.I was able to fix it as follows:
In the corresponding example app folder (e.g.
./examples/liveobjects-live-counter/javascript
) run:node_modules
yarn cache clean ably
yarn --update-checksums
yarn install
yarn run dev
should launch it successfullySummary by CodeRabbit