Skip to content
This repository has been archived by the owner on Apr 9, 2021. It is now read-only.

RPC: Client side callbacks with server-applied arguments (+ more robust classic RPC) #34

Merged
merged 5 commits into from
Feb 7, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
2 changes: 1 addition & 1 deletion examples/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"jest": "^24.9.0",
"metro-react-native-babel-preset": "^0.54.1",
"react-test-renderer": "16.9.0",
"typescript": "^3.5.3"
"typescript": "~3.7.0"
},
"jest": {
"preset": "react-native",
Expand Down
2 changes: 1 addition & 1 deletion examples/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"nars-common": "1.0.0-alpha.2"
},
"devDependencies": {
"typescript": "^3.5.3"
"typescript": "~3.7.0"
}
}
6 changes: 3 additions & 3 deletions examples/common/src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { InputProp } from "nars-common";
*/
export const config = {
Form: {
isCompany: InputProp.optional(InputProp.boolean)
isCompany: InputProp.optional(InputProp.boolean),
},
Feed: {
backgroundColor: InputProp.string
backgroundColor: InputProp.string,
},
ProgressBar: {
height: InputProp.number
height: InputProp.number,
},
};
83 changes: 58 additions & 25 deletions examples/server/Feed.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react";
import { FlatList, Wait } from "nars";
import { FlatList, Wait, View } from "nars";
import { Post, PostType, generatePosts } from "./postGenerator";
import Loading from "./components/Loading";
import TextPost from "./components/TextPost";
Expand All @@ -16,31 +16,64 @@ const renderItem = ({ item }: { item: Post }) => {
}
};

function Feed(props: { backgroundColor: string }) {
const [posts, setPosts] = React.useState(generatePosts);
const [isWaiting, setIsWaiting] = React.useState(false);
const loadMore = () => {
setIsWaiting(true);
setPosts([...posts, ...generatePosts()]);
setTimeout(() => {
setIsWaiting(false);
}, 3000);
type FetchStatus = ["pending"] | ["success", Post[]] | ["error"];

const fetchPosts = (timeout: number) => {
let status: FetchStatus = ["pending"];
let allPosts: Post[] = [];
const fetch = () =>
new Promise<Post[]>(resolve => {
setTimeout(() => {
resolve(generatePosts());
}, timeout);
});

const suspender = fetch().then(
res => {
status = ["success", res];
},
_err => {
status = ["error"];
}
);

const read = () => {
switch (status[0]) {
case "pending":
throw suspender;
case "error":
throw "error";
case "success":
allPosts = [...allPosts, ...status[1]];
return allPosts;
}
};

if (isWaiting) {
return <Wait />;
} else {
return (
<FlatList
style={{ backgroundColor: props.backgroundColor }}
onEndReached={loadMore}
onEndReachedThreshold={0.3}
data={[...posts, { id: "loading", type: PostType.Loading }] as Post[]}
keyExtractor={item => item.item.id}
renderItem={renderItem}
/>
);
}
return { read };
};

const posts = fetchPosts(20000);

function Feed(props: { backgroundColor: string }) {
const allPosts = posts.read();

return (
<FlatList
style={{ backgroundColor: props.backgroundColor }}
onEndReachedThreshold={0.3}
data={[...allPosts, { id: "loading", type: PostType.Loading }] as Post[]}
keyExtractor={item => item.item.id}
renderItem={renderItem}
/>
);
}

function FeedSuspense(props: { backgroundColor: string }) {
return (
<React.Suspense fallback={<Wait />}>
<Feed backgroundColor={props.backgroundColor} />
</React.Suspense>
);
}

export default Feed;
export default FeedSuspense;
2 changes: 1 addition & 1 deletion examples/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
"nodemon": "^2.0.2",
"parcel-bundler": "^1.12.3",
"prettier": "^1.18.2",
"typescript": "^3.5.3"
"typescript": "~3.7.0"
}
}
2 changes: 1 addition & 1 deletion packages/integration_tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
"ts-jest": "^24.1.0",
"prettier": "^1.18.2",
"react-native-typescript-transformer": "^1.2.13",
"typescript": "^3.5.3"
"typescript": "~3.7.0"
}
}
178 changes: 178 additions & 0 deletions packages/integration_tests/src/CallbackProp.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
jest.mock("react-native", () => ({
FlatList: "FlatList",
TouchableOpacity: "TouchableOpacity",
}));
jest.mock("react-native-reanimated", () => ({}));

import * as React from "react";
import { TouchableOpacity, rpc } from "nars";
import { ReactTestInstance, act } from "react-test-renderer";
import { InputProp } from "nars-common";
import {
createRemoteComponent,
render,
getChildren,
testSocket,
testSocketWithMessageSpy,
TestSocketLike,
} from "./TestRenderer";

const config = {
TestComponentSimpleCallback: {
serverToClient: InputProp.function({}),
},
TestComponentCallbackWithValues: {
serverToClient: InputProp.function({
a: InputProp.number,
b: InputProp.number,
}),
},
TestComponentCallbackWithReturnCallback: {
serverToClient: InputProp.function({
a: InputProp.number,
clientToServer: InputProp.function({
b: InputProp.number,
}),
}),
},
};

const components = {
TestComponentSimpleCallback: (props: { serverToClient: (_: {}) => void }) => {
return (
<TouchableOpacity
onPress={rpc(() => {
props.serverToClient({});
})}
/>
);
},
TestComponentCallbackWithValues: (props: {
serverToClient: (_: { a: number; b: number }) => void;
}) => {
return (
<TouchableOpacity
onPress={rpc(() => {
props.serverToClient({ a: 1, b: 2 });
})}
/>
);
},
TestComponentCallbackWithReturnCallback: (props: {
serverToClient: (_: {
a: number;
clientToServer: (_: { b: number }) => void;
}) => void;
}) => {
const [state, setState] = React.useState(5);
return (
<>
<TouchableOpacity
onPress={rpc(() => {
props.serverToClient({
a: 5,
clientToServer: (x: { b: number }) => {
setState(x.b);
},
});
})}
/>
{String(state)}
</>
);
},
};

const [RemoteComponent, createServer] = createRemoteComponent(
config,
components
);

describe("Callback", () => {
let sendCounter = 0;
let counter = 0;
let socket: TestSocketLike;
beforeEach(() => {
sendCounter = 0;
counter = 0;
socket = testSocketWithMessageSpy(
testSocket(),
_ev => {
counter += 1;
},
_data => {
sendCounter += 1;
}
);
createServer(socket);
});
test("No parameters", () => {
let called = false;
const rendered = render(
<RemoteComponent
name="TestComponentSimpleCallback"
webSocket={socket}
props={{
serverToClient: () => {
called = true;
},
}}
/>
);
(getChildren(rendered)[0] as ReactTestInstance).props.onPress();
expect(called).toBe(true);
// Prop change
expect(sendCounter).toEqual(2);
expect(counter).toEqual(2);
});
test("With two parameters", () => {
let result: number[] = [];
const rendered = render(
<RemoteComponent
name="TestComponentCallbackWithValues"
webSocket={socket}
props={{
serverToClient: ({ a, b }: { a: number; b: number }) => {
result = [a, b];
},
}}
/>
);
(getChildren(rendered)[0] as ReactTestInstance).props.onPress();
expect(result).toEqual([1, 2]);
// Prop change
expect(sendCounter).toEqual(2);
expect(counter).toEqual(2);
});
test("With a return callback", () => {
let result: number[] = [];
const rendered = render(
<RemoteComponent
name="TestComponentCallbackWithReturnCallback"
webSocket={socket}
props={{
serverToClient: ({
a,
clientToServer,
}: {
a: number;
clientToServer: (_: { b: number }) => void;
}) => {
result = [a];
clientToServer({ b: 100 });
},
}}
/>
);
expect(sendCounter).toEqual(1);
expect(counter).toEqual(1);
act(() => {
(getChildren(rendered)[0] as ReactTestInstance).props.onPress();
});
expect(result).toEqual([5]);
expect(getChildren(rendered)[1] as ReactTestInstance).toEqual("100");
// Prop change
expect(sendCounter).toEqual(3);
expect(counter).toEqual(3);
});
});
Loading