forked from sagi/seepur
book reading PoC
This commit is contained in:
parent
21ef1e25b2
commit
74ed7612d6
14 changed files with 144 additions and 23 deletions
|
@ -45,6 +45,10 @@ class ClientApiController {
|
|||
return child;
|
||||
}
|
||||
|
||||
async getBooks() {}
|
||||
|
||||
async getCallBooks() {}
|
||||
|
||||
async createCall({auth, request, response}) {
|
||||
try {
|
||||
const user = auth.user;
|
||||
|
|
|
@ -93,7 +93,9 @@ class CallSession {
|
|||
socket.on('wrtc:sdp:offer', this.onSdpOffer.bind(this));
|
||||
socket.on('wrtc:sdp:answer', this.onSdpAnswer.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') {
|
||||
await this.sendStandby(socket, userIndex);
|
||||
// Send event to other user about the call
|
||||
|
@ -160,14 +162,14 @@ class CallSession {
|
|||
const {from = payload.id, ice} = payload;
|
||||
const to = from === 1 ? 2 : 1;
|
||||
this[`user_${to}`].socket.emit('wrtc:ice', {ice});
|
||||
console.log(`[Signal] [onIceCandidate] ${from} -> ${to}`)
|
||||
console.log(`[Signal] [onIceCandidate] ${from} -> ${to}`);
|
||||
return true;
|
||||
}
|
||||
async onSdpOffer(payload) {
|
||||
const {from = payload.id, sdp} = payload;
|
||||
const to = from === 1 ? 2 : 1;
|
||||
this[`user_${to}`].socket.emit('wrtc:sdp:offer', {sdp});
|
||||
console.log(`[Signal] [onSdpOffer] ${from} -> ${to}`)
|
||||
console.log(`[Signal] [onSdpOffer] ${from} -> ${to}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -175,7 +177,16 @@ class CallSession {
|
|||
const {from = payload.id, sdp} = payload;
|
||||
const to = from === 1 ? 2 : 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
12
app/Models/Book.js
Normal file
12
app/Models/Book.js
Normal 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
|
22
database/migrations/1586821478599_book_schema.js
Normal file
22
database/migrations/1586821478599_book_schema.js
Normal 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
|
|
@ -39,6 +39,7 @@
|
|||
"adonis-vue-websocket": "^2.0.2",
|
||||
"animate.css": "^3.7.2",
|
||||
"bulma": "^0.8.0",
|
||||
"flipbook-vue": "^0.8.1",
|
||||
"fork-awesome": "^1.1.7",
|
||||
"moment": "^2.24.0",
|
||||
"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
|
@ -6914,3 +6914,7 @@ label.panel-block {
|
|||
opacity: 0; }
|
||||
100% {
|
||||
opacity: 1; } }
|
||||
|
||||
.flipbook {
|
||||
width: 100%;
|
||||
height: 60vh; }
|
||||
|
|
|
@ -223,3 +223,8 @@ $positions: (
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
// TODO: Remove this - make generic
|
||||
.flipbook {
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import WebSocketService from "../scripts/websocket.service";
|
|||
import { EventEmitter } from "events";
|
||||
export default class CallManager {
|
||||
private inCall: boolean;
|
||||
private peerId: number;
|
||||
public peerId: number;
|
||||
private localStream: MediaStream;
|
||||
private remoteStream: MediaStream;
|
||||
private signalingChannel;
|
||||
|
@ -16,6 +16,7 @@ export default class CallManager {
|
|||
this.remoteStream = new MediaStream();
|
||||
}
|
||||
|
||||
|
||||
async connectToCall(mediaConstraints: MediaStreamConstraints): Promise<boolean> {
|
||||
if (this.inCall) throw new Error('Already connected 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:answer', self.onRemoteAnswer.bind(self));
|
||||
signalingChannel.on('wrtc:ice', self.onRemoteIce.bind(self));
|
||||
signalingChannel.on('book:action:flip-page', self.onActionBookFlip.bind(self))
|
||||
signalingChannel.on('error', (e) => {
|
||||
console.error(e);
|
||||
resolve(false)
|
||||
|
@ -150,6 +152,10 @@ export default class CallManager {
|
|||
return this.remoteStream;
|
||||
}
|
||||
|
||||
onActionBookFlip(payload) {
|
||||
this.emit(ECallEvents.ACTION_BOOK_FLIP, payload);
|
||||
};
|
||||
|
||||
close() {
|
||||
console.log('Closing...');
|
||||
if (!this.inCall) return;
|
||||
|
@ -162,9 +168,11 @@ export default class CallManager {
|
|||
this.remoteStream = null;
|
||||
this.inCall = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export enum ECallEvents {
|
||||
CLOSE = 'CLOSE',
|
||||
REMOTE_STREAM = 'REMOTE_STREAM'
|
||||
REMOTE_STREAM = 'REMOTE_STREAM',
|
||||
ACTION_BOOK_FLIP = 'ACTION_BOOK_FLIP'
|
||||
}
|
||||
|
|
|
@ -3,15 +3,28 @@
|
|||
<div v-if="loading">
|
||||
<Loading />
|
||||
</div>
|
||||
<div v-else class="is-flex">
|
||||
<video
|
||||
:src-object.prop.camel="localStream"
|
||||
autoplay
|
||||
playsinline
|
||||
muted="true"
|
||||
style="max-width:40%"
|
||||
/>
|
||||
<video :src-object.prop.camel="remoteStream" autoplay style="max-width:40%" />
|
||||
<div v-else class>
|
||||
<div class="video-strip is-flex">
|
||||
<video
|
||||
:src-object.prop.camel="localStream"
|
||||
autoplay
|
||||
playsinline
|
||||
muted="true"
|
||||
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>
|
||||
</template>
|
||||
|
@ -20,11 +33,13 @@
|
|||
import Loading from "../../shared/components/Loading/Loading.vue";
|
||||
import WebsocketService from "../scripts/websocket.service";
|
||||
import CallManager, { ECallEvents } from "../classes/call.manager";
|
||||
import Flipbook from "flipbook-vue";
|
||||
import Services from "../../services/index";
|
||||
import { mapActions, mapGetters } from "vuex";
|
||||
export default {
|
||||
components: {
|
||||
Loading
|
||||
Loading,
|
||||
Flipbook
|
||||
},
|
||||
name: "Call",
|
||||
|
||||
|
@ -35,6 +50,7 @@ export default {
|
|||
const ws = await WebsocketService.getInstance();
|
||||
this.callManager = new CallManager(ws, callId);
|
||||
this.callManager.on(ECallEvents.CLOSE, this.callEnded);
|
||||
this.callManager.on(ECallEvents.ACTION_BOOK_FLIP, this.onRemoteFlip);
|
||||
const success = await this.callManager.connectToCall({
|
||||
video: true,
|
||||
audio: true
|
||||
|
@ -63,6 +79,27 @@ export default {
|
|||
async setupCall(): Promise<boolean> {
|
||||
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) {
|
||||
this.notify({ message: `Call #${callId} Ended` });
|
||||
this.$router.push({ path: `/` });
|
||||
|
@ -74,6 +111,10 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
book: {
|
||||
title: "Taylor",
|
||||
pages: 34
|
||||
},
|
||||
loading: true,
|
||||
localStream: null,
|
||||
remoteStream: null,
|
||||
|
@ -84,3 +125,4 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -3142,6 +3142,13 @@ flagged-respawn@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41"
|
||||
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:
|
||||
version "1.1.1"
|
||||
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:
|
||||
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:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
|
||||
|
|
Loading…
Reference in a new issue