Skip to content

Commit

Permalink
progress implementing phx protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
floodfx committed Jan 17, 2022
1 parent 7cd17c1 commit 6ccb659
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 19 deletions.
10 changes: 10 additions & 0 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ const app: Application = createExpressServer({
const server = new HTTPServer(app);
const io = new SocketServer(server);

// io.on('connection', (socket) => {
// console.log('a user connected');
// });

io.on('connection', (socket) => {
socket.onAny((...args) => {
console.log("any event", args);
})
});

useSocketServer(io, {
controllers: [path.join(__dirname, '/socket/*controller.js')]
});
Expand Down
24 changes: 17 additions & 7 deletions src/server/live/poc_liveview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ export class POCLiveViewComponent implements
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
socket.on("message_saved", function (message) {
console.log("Saved message received back: ", message);
socket.on("connect", () => {
console.log(socket.id); //
socket.emit("phx_join", ["4", "4", "lv:phx-FsrMvfqMqliBmgJF", "phx_join", {
params: {_csrf_token: "OnAPAm1NKwgPCQg4TAUMdnx3WSI6fj8Jm2Zk8yoEVhOgxUSG72hFT7se", _mounts: 0},
session: "SFMyNTY.g2gDaAJhBXQAAAAIZAACaWRtAAAAFHBoeC1Gc3JNdmZxTXFsaUJtZ0pGZAAMbGl2ZV9zZXNzaW9uaAJkAAdkZWZhdWx0bggAyL-egcCNyhZkAApwYXJlbnRfcGlkZAADbmlsZAAIcm9vdF9waWRkAANuaWxkAAlyb290X3ZpZXdkACJFbGl4aXIuTGl2ZVZpZXdTdHVkaW9XZWIuTGlnaHRMaXZlZAAGcm91dGVyZAAfRWxpeGlyLkxpdmVWaWV3U3R1ZGlvV2ViLlJvdXRlcmQAB3Nlc3Npb250AAAAAGQABHZpZXdkACJFbGl4aXIuTGl2ZVZpZXdTdHVkaW9XZWIuTGlnaHRMaXZlbgYAFjWyY34BYgABUYA.9ZElHc0CEv9Nu5S1JCoidU2S0uDDBqN1IsTcOMFTCrg",
static: "SFMyNTY.g2gDaAJhBXQAAAADZAAKYXNzaWduX25ld2pkAAVmbGFzaHQAAAAAZAACaWRtAAAAFHBoeC1Gc3JNdmZxTXFsaUJtZ0pGbgYAFjWyY34BYgABUYA.X73SFphVgh0CYME6vGp47u3Q4jKg5Rh6i_UCQEdl0ac",
url: "http://localhost:4000/light"}
]);
});
socket.on("phx_reply", function (message) {
console.log("Reply: ", message);
});
function onClick() {
socket.emit("on", { text: "Turn on" });
socket.emit("event", ["4", "64", "lv:phx-FssjlAMwe9RhogDq", "event", {type: "click", event: "on", value: {value: ""}}]);
}
</script>
<h1>Front Porch Light</h1>
Expand All @@ -36,19 +46,19 @@ export class POCLiveViewComponent implements
</div>
<button phx-click="off">
<img src="images/light-off.svg">
Off
</button>
<button phx-click="down">
<img src="images/down.svg">
Down
</button>
<button phx-click="up">
<img src="images/up.svg">
Up
</button>
<button phx-click="on" onClick="onClick()">
<img src="images/light-on.svg">
On
</button>
</div>
`
Expand Down
13 changes: 11 additions & 2 deletions src/server/liveview/templates/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,21 @@ function escapehtml(unsafe: unknown): string {

export class HtmlSafeString {
readonly statics: readonly string[]
readonly dynamics: readonly unknown[]
readonly _dynamics: readonly unknown[]
readonly children: readonly HtmlSafeString[]

constructor(statics: readonly string[], dynamics: readonly unknown[]) {
this.statics = statics
this.dynamics = dynamics
this._dynamics = dynamics
}

get dynamics(): readonly string[] {
return this._dynamics.map((d) => {
if (d instanceof HtmlSafeString) {
return d.toString()
}
return String(d)
})
}

toString(): string {
Expand Down
163 changes: 153 additions & 10 deletions src/server/socket/poc_socket_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,169 @@ import {
OnDisconnect,
MessageBody,
OnMessage,
SocketIO,
SocketId,
} from 'socket-controllers';
import { Socket } from 'socket.io';
import { POCLiveViewComponent } from '../live/poc_liveview';

enum PhxSocketProtocolNames {
joinRef = 0,
messageRef,
topic,
event,
payload
}

type PhxSocketProtocol<Payload> = [
joinRef: number,
messageRef: number,
topic: number,
event: "phx_reply" | string,
payload: Payload
]

interface PhxJoinPayload {
params: { _csrf: string } & { [key: string]: string }
session: string
static: string
url: string
}

type PhxJoin = PhxSocketProtocol<PhxJoinPayload>;


type Dynamics = { [key: number]: string | Dynamics }

type RenderedNode = { [key: number]: string | RenderedNode } & { [key in "s"]: readonly string[] }

interface Rendered {
rendered: RenderedNode
}

interface Diff {
diff: Dynamics
}

interface PhxReplyPayload {
response: Rendered | Diff | {}
status: "ok"
}

type PhxReply = PhxSocketProtocol<PhxReplyPayload>;

interface PhxEventPayload<T> {
type: string,
event: string,
value: T
}

type PhxEvent<T> = PhxSocketProtocol<PhxEventPayload<T>>

@SocketController()
export class POCSocketController {

@OnConnect()
connection(@ConnectedSocket() socket: any) {
console.log('client connected');
connection(@ConnectedSocket() socket: any, @SocketIO() io: Socket, @SocketId() socketId: string) {
console.log('client connected:', socketId);

// run mount here?
}

@OnDisconnect()
disconnect(@ConnectedSocket() socket: any) {
console.log('client disconnected');
@OnMessage('phx_join')
phx_join(@ConnectedSocket() socket: any, @SocketId() socketId: string, @MessageBody() message: PhxJoin) {

// get current context for this socketid
// render the view and send response
const poc = new POCLiveViewComponent();
const pocCtx = poc.mount({}, {}, {});
const pocView = poc.render(pocCtx);

// map array of dynamics to object with indiceies as keys
const dynamics = pocView.dynamics.reduce((acc: { [key: number]: unknown }, cur, index: number) => {
acc[index] = cur;
return acc;
}, {} as { [key: number]: unknown })

const rendered: Rendered = {
rendered: {
...dynamics,
s: pocView.statics
}
}

const reply: PhxReply = [
message[0],
message[1],
message[2],
"phx_reply",
{
response: {
...rendered
},
status: "ok"
}
]
console.log("sending phx_reply", reply);
socket.emit('phx_reply', reply);
}

@OnMessage('on')
on(@ConnectedSocket() socket: any, @MessageBody() message: any) {
@OnMessage('event')
on(@ConnectedSocket() socket: any, @MessageBody() message: PhxEvent<any>) {
// update context
// rerun render
// send back dynamics if they changed
console.log('socket:');
console.log('message:', message);
console.log('setting id to the message and sending it back to the client');
message.id = 1;
socket.emit('message_saved', message);

const poc = new POCLiveViewComponent();
const pocCtx = poc.mount({}, {}, {}); // Look up current ctx

let brightness = pocCtx.data.brightness;
const payload = message[PhxSocketProtocolNames.payload];

switch (payload.event) {
case "on":
brightness = 100;
break;
case "off":
brightness = 0;
break;
}

const pocView = poc.render({ ...pocCtx, data: { brightness } });

// map array of dynamics to object with indiceies as keys
const dynamics = pocView.dynamics.reduce((acc: { [key: number]: string }, cur: string, index: number) => {
acc[index] = cur;
return acc;
}, {} as { [key: string]: string })

const diff: Diff = {
diff: {
...dynamics
}
}

const reply: PhxReply = [
message[0],
message[1],
message[2],
"phx_reply",
{
response: {
...diff
},
status: "ok"
}
]
console.log("sending phx_reply", reply);
socket.emit('phx_reply', reply);
}

@OnDisconnect()
disconnect(@ConnectedSocket() socket: any) {
console.log('client disconnected');
}

}

0 comments on commit 6ccb659

Please sign in to comment.