-
Notifications
You must be signed in to change notification settings - Fork 12
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
A bunch of cleanups backported from my durable objects prototype #56
Changes from all commits
385b5d3
a20b924
7f327d9
d27f89f
add66bc
70e9d27
30d1d44
db67831
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import type { JSONValue, ScanResult, WriteTransaction } from "replicache"; | ||
import { delObject, getObject, putObject } from "./data"; | ||
import { ExecuteStatementFn, transact } from "./rds"; | ||
|
||
/** | ||
* Implements ReplicaCache's WriteTransaction interface in terms of a MySQL | ||
* transaction. | ||
*/ | ||
export class WriteTransactionImpl implements WriteTransaction { | ||
private _docID: string; | ||
private _executor: ExecuteStatementFn; | ||
private _cache: Record< | ||
string, | ||
{ value: JSONValue | undefined; dirty: boolean } | ||
> = {}; | ||
|
||
constructor(executor: ExecuteStatementFn, docID: string) { | ||
this._docID = docID; | ||
this._executor = executor; | ||
} | ||
|
||
async put(key: string, value: JSONValue): Promise<void> { | ||
this._cache[key] = { value, dirty: true }; | ||
} | ||
async del(key: string): Promise<boolean> { | ||
const had = await this.has(key); | ||
this._cache[key] = { value: undefined, dirty: true }; | ||
return had; | ||
} | ||
async get(key: string): Promise<JSONValue | undefined> { | ||
const entry = this._cache[key]; | ||
if (entry) { | ||
return entry.value; | ||
} | ||
const value = await getObject(this._executor, this._docID, key); | ||
this._cache[key] = { value, dirty: false }; | ||
return value; | ||
} | ||
async has(key: string): Promise<boolean> { | ||
const val = await this.get(key); | ||
return val !== undefined; | ||
} | ||
|
||
// TODO! | ||
async isEmpty(): Promise<boolean> { | ||
throw new Error("not implemented"); | ||
} | ||
scan(): ScanResult<string> { | ||
throw new Error("not implemented"); | ||
} | ||
scanAll(): Promise<[string, JSONValue][]> { | ||
throw new Error("not implemented"); | ||
} | ||
|
||
async flush(): Promise<void> { | ||
await Promise.all( | ||
Object.entries(this._cache) | ||
.filter(([, { dirty }]) => dirty) | ||
.map(([k, { value }]) => { | ||
if (value === undefined) { | ||
return delObject(this._executor, this._docID, k); | ||
} else { | ||
return putObject(this._executor, this._docID, k, value); | ||
} | ||
}) | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import { Data } from "./data"; | ||
import styles from "./collaborator.module.css"; | ||
import { useEffect, useState } from "react"; | ||
import { Rect } from "./rect"; | ||
import { useCursor } from "./smoothie"; | ||
import { Replicache } from "replicache"; | ||
import { M } from "./mutators"; | ||
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. import type and use longer name? 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. Huh, I wonder why ts didn't complain about that. I am going to try on the short name for size. In applications, |
||
import { useClientInfo } from "./subscriptions"; | ||
|
||
const hideCollaboratorDelay = 5000; | ||
|
||
|
@@ -15,17 +17,17 @@ interface Position { | |
} | ||
|
||
export function Collaborator({ | ||
data, | ||
rep, | ||
clientID, | ||
}: { | ||
data: Data; | ||
rep: Replicache<M>; | ||
clientID: string; | ||
}) { | ||
const clientInfo = data.useClientInfo(clientID); | ||
const clientInfo = useClientInfo(rep, clientID); | ||
const [lastPos, setLastPos] = useState<Position | null>(null); | ||
const [gotFirstChange, setGotFirstChange] = useState(false); | ||
const [, setPoke] = useState({}); | ||
const cursor = useCursor(data.rep, clientID); | ||
const cursor = useCursor(rep, clientID); | ||
|
||
let curPos = null; | ||
let userInfo = null; | ||
|
@@ -76,7 +78,7 @@ export function Collaborator({ | |
{clientInfo.selectedID && ( | ||
<Rect | ||
{...{ | ||
data, | ||
rep, | ||
key: `selection-${clientInfo.selectedID}`, | ||
id: clientInfo.selectedID, | ||
highlight: true, | ||
|
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.
General rule is to use Maps for maps.
Otherwise you run into issues with keys like
toString
etc