book reading PoC

This commit is contained in:
Sagi Dayan 2020-04-13 20:56:04 -04:00
parent 21ef1e25b2
commit 74ed7612d6
14 changed files with 144 additions and 23 deletions

View file

@ -45,6 +45,10 @@ class ClientApiController {
return child; return child;
} }
async getBooks() {}
async getCallBooks() {}
async createCall({auth, request, response}) { async createCall({auth, request, response}) {
try { try {
const user = auth.user; const user = auth.user;

View file

@ -93,7 +93,9 @@ class CallSession {
socket.on('wrtc:sdp:offer', this.onSdpOffer.bind(this)); socket.on('wrtc:sdp:offer', this.onSdpOffer.bind(this));
socket.on('wrtc:sdp:answer', this.onSdpAnswer.bind(this)); socket.on('wrtc:sdp:answer', this.onSdpAnswer.bind(this));
socket.on('wrtc:ice', this.onIceCandidate.bind(this)); socket.on('wrtc:ice', this.onIceCandidate.bind(this));
await this.updateState(); socket
.on('book:action:flip-page', this.onActionBookFlip.bind(this))
await this.updateState();
if (this.state === 'STARTED') { if (this.state === 'STARTED') {
await this.sendStandby(socket, userIndex); await this.sendStandby(socket, userIndex);
// Send event to other user about the call // Send event to other user about the call
@ -160,14 +162,14 @@ class CallSession {
const {from = payload.id, ice} = payload; const {from = payload.id, ice} = payload;
const to = from === 1 ? 2 : 1; const to = from === 1 ? 2 : 1;
this[`user_${to}`].socket.emit('wrtc:ice', {ice}); this[`user_${to}`].socket.emit('wrtc:ice', {ice});
console.log(`[Signal] [onIceCandidate] ${from} -> ${to}`) console.log(`[Signal] [onIceCandidate] ${from} -> ${to}`);
return true; return true;
} }
async onSdpOffer(payload) { async onSdpOffer(payload) {
const {from = payload.id, sdp} = payload; const {from = payload.id, sdp} = payload;
const to = from === 1 ? 2 : 1; const to = from === 1 ? 2 : 1;
this[`user_${to}`].socket.emit('wrtc:sdp:offer', {sdp}); this[`user_${to}`].socket.emit('wrtc:sdp:offer', {sdp});
console.log(`[Signal] [onSdpOffer] ${from} -> ${to}`) console.log(`[Signal] [onSdpOffer] ${from} -> ${to}`);
return true; return true;
} }
@ -175,7 +177,16 @@ class CallSession {
const {from = payload.id, sdp} = payload; const {from = payload.id, sdp} = payload;
const to = from === 1 ? 2 : 1; const to = from === 1 ? 2 : 1;
this[`user_${to}`].socket.emit('wrtc:sdp:answer', {sdp}); this[`user_${to}`].socket.emit('wrtc:sdp:answer', {sdp});
console.log(`[Signal] [onSdpAnswer] ${from} -> ${to}`) console.log(`[Signal] [onSdpAnswer] ${from} -> ${to}`);
return true;
}
onActionBookFlip(payload) {
const {from = payload.id, direction} = payload;
const to = from === 1 ? 2 : 1;
this[`user_${to}`].socket.emit('book:action:flip-page', {direction});
console.log(
`[Signal] [book] [action] [flip] [${direction}] ${from} -> ${to}`);
return true; return true;
} }
} }

12
app/Models/Book.js Normal file
View file

@ -0,0 +1,12 @@
'use strict'
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
class Book extends Model {
user() {
return this.belongsTo('App/Models/User');
}
}
module.exports = Book

View file

@ -0,0 +1,22 @@
'use strict'
/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class BookSchema extends Schema {
up() {
this.create('books', (table) => {
table.increments()
table.bigInteger('user_id').notNullable();
table.integer('pages').notNullable();
table.string('book_folder').notNullable();
table.timestamps()
})
}
down() {
this.drop('books')
}
}
module.exports = BookSchema

View file

@ -39,6 +39,7 @@
"adonis-vue-websocket": "^2.0.2", "adonis-vue-websocket": "^2.0.2",
"animate.css": "^3.7.2", "animate.css": "^3.7.2",
"bulma": "^0.8.0", "bulma": "^0.8.0",
"flipbook-vue": "^0.8.1",
"fork-awesome": "^1.1.7", "fork-awesome": "^1.1.7",
"moment": "^2.24.0", "moment": "^2.24.0",
"regenerator-runtime": "^0.13.5", "regenerator-runtime": "^0.13.5",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -6914,3 +6914,7 @@ label.panel-block {
opacity: 0; } opacity: 0; }
100% { 100% {
opacity: 1; } } opacity: 1; } }
.flipbook {
width: 100%;
height: 60vh; }

View file

@ -223,3 +223,8 @@ $positions: (
opacity: 1; opacity: 1;
} }
} }
// TODO: Remove this - make generic
.flipbook {
width: 100%;
height: 60vh;
}

View file

@ -2,7 +2,7 @@ import WebSocketService from "../scripts/websocket.service";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
export default class CallManager { export default class CallManager {
private inCall: boolean; private inCall: boolean;
private peerId: number; public peerId: number;
private localStream: MediaStream; private localStream: MediaStream;
private remoteStream: MediaStream; private remoteStream: MediaStream;
private signalingChannel; private signalingChannel;
@ -16,6 +16,7 @@ export default class CallManager {
this.remoteStream = new MediaStream(); this.remoteStream = new MediaStream();
} }
async connectToCall(mediaConstraints: MediaStreamConstraints): Promise<boolean> { async connectToCall(mediaConstraints: MediaStreamConstraints): Promise<boolean> {
if (this.inCall) throw new Error('Already connected to call'); if (this.inCall) throw new Error('Already connected to call');
console.log('connecting to call'); console.log('connecting to call');
@ -30,6 +31,7 @@ export default class CallManager {
signalingChannel.on('wrtc:sdp:offer', self.onRemoteOffer.bind(self)); signalingChannel.on('wrtc:sdp:offer', self.onRemoteOffer.bind(self));
signalingChannel.on('wrtc:sdp:answer', self.onRemoteAnswer.bind(self)); signalingChannel.on('wrtc:sdp:answer', self.onRemoteAnswer.bind(self));
signalingChannel.on('wrtc:ice', self.onRemoteIce.bind(self)); signalingChannel.on('wrtc:ice', self.onRemoteIce.bind(self));
signalingChannel.on('book:action:flip-page', self.onActionBookFlip.bind(self))
signalingChannel.on('error', (e) => { signalingChannel.on('error', (e) => {
console.error(e); console.error(e);
resolve(false) resolve(false)
@ -150,6 +152,10 @@ export default class CallManager {
return this.remoteStream; return this.remoteStream;
} }
onActionBookFlip(payload) {
this.emit(ECallEvents.ACTION_BOOK_FLIP, payload);
};
close() { close() {
console.log('Closing...'); console.log('Closing...');
if (!this.inCall) return; if (!this.inCall) return;
@ -162,9 +168,11 @@ export default class CallManager {
this.remoteStream = null; this.remoteStream = null;
this.inCall = false; this.inCall = false;
} }
} }
export enum ECallEvents { export enum ECallEvents {
CLOSE = 'CLOSE', CLOSE = 'CLOSE',
REMOTE_STREAM = 'REMOTE_STREAM' REMOTE_STREAM = 'REMOTE_STREAM',
ACTION_BOOK_FLIP = 'ACTION_BOOK_FLIP'
} }

View file

@ -3,15 +3,28 @@
<div v-if="loading"> <div v-if="loading">
<Loading /> <Loading />
</div> </div>
<div v-else class="is-flex"> <div v-else class>
<video <div class="video-strip is-flex">
:src-object.prop.camel="localStream" <video
autoplay :src-object.prop.camel="localStream"
playsinline autoplay
muted="true" playsinline
style="max-width:40%" muted="true"
/> style="max-width:40%"
<video :src-object.prop.camel="remoteStream" autoplay style="max-width:40%" /> />
<video :src-object.prop.camel="remoteStream" autoplay style="max-width:40%" />
</div>
<div class>
<flipbook
class="flipbook"
:pages="createPages(book)"
forwardDirection="left"
:zooms="null"
@flip-left-end="onFlip('left')"
@flip-right-end="onFlip('right')"
ref="flipbook"
></flipbook>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -20,11 +33,13 @@
import Loading from "../../shared/components/Loading/Loading.vue"; import Loading from "../../shared/components/Loading/Loading.vue";
import WebsocketService from "../scripts/websocket.service"; import WebsocketService from "../scripts/websocket.service";
import CallManager, { ECallEvents } from "../classes/call.manager"; import CallManager, { ECallEvents } from "../classes/call.manager";
import Flipbook from "flipbook-vue";
import Services from "../../services/index"; import Services from "../../services/index";
import { mapActions, mapGetters } from "vuex"; import { mapActions, mapGetters } from "vuex";
export default { export default {
components: { components: {
Loading Loading,
Flipbook
}, },
name: "Call", name: "Call",
@ -35,6 +50,7 @@ export default {
const ws = await WebsocketService.getInstance(); const ws = await WebsocketService.getInstance();
this.callManager = new CallManager(ws, callId); this.callManager = new CallManager(ws, callId);
this.callManager.on(ECallEvents.CLOSE, this.callEnded); this.callManager.on(ECallEvents.CLOSE, this.callEnded);
this.callManager.on(ECallEvents.ACTION_BOOK_FLIP, this.onRemoteFlip);
const success = await this.callManager.connectToCall({ const success = await this.callManager.connectToCall({
video: true, video: true,
audio: true audio: true
@ -63,6 +79,27 @@ export default {
async setupCall(): Promise<boolean> { async setupCall(): Promise<boolean> {
return true; return true;
}, },
onFlip(direction: "left" | "right") {
if (this.callManager.peerId == 2)
this.callManager.send(`book:action:flip-page`, { direction });
},
onRemoteFlip(payload) {
switch (payload.direction) {
case "left":
this.$refs.flipbook.flipLeft();
break;
case "right":
this.$refs.flipbook.flipRight();
break;
}
},
createPages(book) {
const pages = [null];
for (let i = 1; i < book.pages + 1; i++) {
pages.push(`/u/images/${i}.JPG`);
}
return pages;
},
callEnded(callId) { callEnded(callId) {
this.notify({ message: `Call #${callId} Ended` }); this.notify({ message: `Call #${callId} Ended` });
this.$router.push({ path: `/` }); this.$router.push({ path: `/` });
@ -74,6 +111,10 @@ export default {
}, },
data() { data() {
return { return {
book: {
title: "Taylor",
pages: 34
},
loading: true, loading: true,
localStream: null, localStream: null,
remoteStream: null, remoteStream: null,
@ -84,3 +125,4 @@ export default {
}; };
</script> </script>

View file

@ -3142,6 +3142,13 @@ flagged-respawn@^1.0.0:
resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41"
integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==
flipbook-vue@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/flipbook-vue/-/flipbook-vue-0.8.1.tgz#1b3d29a5a8d0d6921ff0be1a91e4165b0d9d6d6e"
integrity sha512-HPWrGe6fwIGSG2IV+kN2Bz16pp8DtsgvNrObxaXtr+XNwxgaLWpbKzM8QV5alBcMFJtyo+7d2Ds1zLrCp8h1LQ==
dependencies:
rematrix "^0.4.1"
flush-write-stream@^1.0.0: flush-write-stream@^1.0.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@ -5993,6 +6000,11 @@ regjsparser@^0.1.4:
dependencies: dependencies:
jsesc "~0.5.0" jsesc "~0.5.0"
rematrix@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/rematrix/-/rematrix-0.4.1.tgz#496d40b48c79dd3820bc40352b8d7bec44b32887"
integrity sha512-nH4OjL09zw4yhdBClK4B93kRWbHu+std7ZV6ljS2Zsgy2YrPcayL67axKLfMxoFool8mPyVY614m8SFE7sJQLw==
remove-trailing-separator@^1.0.1: remove-trailing-separator@^1.0.1:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"