Fixed call. Added lobby signals + book selection signals

This commit is contained in:
Sagi Dayan 2020-04-29 22:34:14 -04:00
parent 0e2fa21511
commit cef526205c
18 changed files with 192 additions and 67 deletions

View file

@ -12,6 +12,15 @@ class BookApiController {
code: 404, message: 'no file'
}
}
async getThumbnail({request, book, response}) {
const file = await FileUtils.getFile(`books/${book.book_folder}/1.jpg`);
if (file)
response.send(file);
else
return {
code: 404, message: 'no file'
}
}
}
module.exports = BookApiController

View file

@ -4,6 +4,7 @@ const User = use('App/Models/User');
const Child = use('App/Models/Child')
const Link = use('App/Models/Link');
const Call = use('App/Models/Call');
const Book = use('App/Models/Book');
const FileUtils = use('App/Utils/FileUtils');
const UserChildUtils = use('App/Utils/UserChildUtils');
@ -13,8 +14,14 @@ class ClientApiController {
async getUser({auth}) {
const user = auth.user.toJSON();
const connections = await UserChildUtils.getUserConnections(user.id);
const booksResponse = await Book.query()
.where({user_id: null})
.orWhere({user_id: user.id})
.fetch();
let books = [];
if (booksResponse.rows.length) books = booksResponse.rows;
return {
...user, connections: {...connections}
...user, connections: {...connections}, books
}
}

View file

@ -112,6 +112,8 @@ class CallSession {
socket.on('wrtc:sdp:answer', this.onSdpAnswer.bind(this));
socket.on('wrtc:ice', this.onIceCandidate.bind(this));
socket.on('call:host:changed', this.onHostChanged.bind(this));
socket.on('call:view:lobby', this.onCallViewLobby.bind(this));
socket.on('call:view:book', this.onCallViewBook.bind(this));
socket.on('book:action:flip-page', this.onActionBookFlip.bind(this));
await this.updateState();
@ -221,6 +223,22 @@ class CallSession {
peerId}`);
return true;
}
onCallViewLobby(payload) {
const {peerId, userId, direction} = payload;
this.userMap.get(peerId).socket.emit('call:view:lobby', {});
console.log(
`[Signal] [call] [view] [lobby] [${direction}] ${userId} -> ${peerId}`);
return true;
}
onCallViewBook(payload) {
const {peerId, userId, direction, bookId} = payload;
this.userMap.get(peerId).socket.emit('call:view:book', {bookId});
console.log(
`[Signal] [call] [view] [book] [${direction}] ${userId} -> ${peerId}`);
return true;
}
async onHostChanged(payload) {
const {peerId, userId} = payload;
this.hostId = this.hostId === userId ? peerId : userId;

View file

@ -0,0 +1,27 @@
'use strict'
/** @typedef {import('@adonisjs/framework/src/Request')} Request */
/** @typedef {import('@adonisjs/framework/src/Response')} Response */
/** @typedef {import('@adonisjs/framework/src/View')} View */
const Book = use('App/Models/Book');
class BookContext {
/**
* @param {object} ctx
* @param {Request} ctx.request
* @param {Function} next
*/
async handle(ctx, next) {
const {request, auth, response} = ctx;
const bookId = request.params.bookId;
const book = await Book.find(bookId);
if (!book) {
response.status(404);
response.send({code: 404, message: 'Book not found'});
return;
}
ctx.book = book;
// call next to advance the request
await next()
}
}
module.exports = BookContext

View file

@ -11,29 +11,23 @@ class BookPageAuth {
* @param {Function} next
*/
async handle(ctx, next) {
const {request, auth, response} = ctx;
const {request, auth, response, book, call} = ctx;
// call next to advance the request
const user = auth.user;
const bookId = request.params.bookId;
const book = await Book.find(bookId);
if (!book) {
response.status(404);
response.send({code: 404, message: 'Book not found'});
return;
}
ctx.book = book;
if (book.user_id) {
// Belongs to a user. Check if the book user has a connection with this
// user
if (book.user_id === user.id)
if (book.user_id === user.id) {
await next();
else {
const ownerConnections =
await UserChildUtils.getUserConnections(book.user_id);
console.log(ownerConnections);
} else if (call.parent_id === user.id || call.guest_id === user.id) {
await next();
} else {
response.status(403);
response.send({code: 403, message: 'Book is private'});
}
} else {
await next();
}
await next();
}
}

View file

@ -0,0 +1,27 @@
'use strict'
/** @typedef {import('@adonisjs/framework/src/Request')} Request */
/** @typedef {import('@adonisjs/framework/src/Response')} Response */
/** @typedef {import('@adonisjs/framework/src/View')} View */
const Call = use('App/Models/Call');
class CallContext {
/**
* @param {object} ctx
* @param {Request} ctx.request
* @param {Function} next
*/
async handle(ctx, next) {
const {request, auth, response} = ctx;
const callId = request.params.callId;
const call = await Call.find(callId);
if (!call) {
response.status(404);
response.send({code: 404, message: 'Call not found'});
return;
}
ctx.call = call;
// call next to advance the request
await next()
}
}
module.exports = CallContext

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

@ -13,7 +13,7 @@ export default class CallManager {
public isHost = false;
public books = [];
public currentActivity = null;
constructor(private ws: WebSocketService, private callId: number, private userId: number) {
constructor(private ws: WebSocketService, readonly callId: number, private userId: number) {
this.inCall = false;
this.peerId = null;
this.pc = null;
@ -37,6 +37,8 @@ export default class CallManager {
signalingChannel.on('wrtc:ice', self.onRemoteIce.bind(self));
signalingChannel.on('book:action:flip-page', self.onActionBookFlip.bind(self));
signalingChannel.on('call:host:changed', self.onRemoteHostChanged.bind(self));
signalingChannel.on('call:view:lobby', self.onRemoteViewLobby.bind(self));
signalingChannel.on('call:view:book', self.onRemoteViewBook.bind(self));
signalingChannel.on('error', (e) => {
console.error(e);
resolve(false)
@ -55,6 +57,7 @@ export default class CallManager {
this.emitter.emit(event, data);
}
private send(event: string, payload: { [key: string]: any }) {
console.log(`Sending event: ${event}`);
this.signalingChannel.emit(event, {
userId: this.userId,
peerId: this.peerId,
@ -105,8 +108,14 @@ export default class CallManager {
return true;
}
public selectBook(index: number) {
public selectBook(index: number, remote: boolean) {
this.currentActivity = this.books[index];
console.log('-------> Selected Book ', index, 'bookId:', this.currentActivity.id);
if (!remote) this.send('call:view:book', { bookId: this.currentActivity.id });
}
public backToLobby() {
console.log('-------> backToLobby');
this.send('call:view:lobby', {});
}
setupPeerConnectionListeners() {
@ -189,6 +198,13 @@ export default class CallManager {
this.emit(ECallEvents.CALL_HOST_CHANGED, payload);
}
onRemoteViewLobby(payload) {
this.emit(ECallEvents.CALL_VIEW_LOBBY, null);
}
onRemoteViewBook(payload) {
this.emit(ECallEvents.CALL_VIEW_BOOK, payload);
}
close() {
console.log('Closing...');
if (!this.inCall) return;
@ -209,4 +225,6 @@ export enum ECallEvents {
REMOTE_STREAM = 'REMOTE_STREAM',
ACTION_BOOK_FLIP = 'ACTION_BOOK_FLIP',
CALL_HOST_CHANGED = "CALL_HOST_CHANGED",
CALL_VIEW_LOBBY = 'CALL_VIEW_LOBBY',
CALL_VIEW_BOOK = 'CALL_VIEW_BOOK',
}

View file

@ -10,7 +10,7 @@ const store = new Store({
state: {
inCall: false,
callManager: null as CallManager,
user: { name: 'loading...', is_admin: false, id: null },
user: { name: 'loading...', is_admin: false, id: null, books: [] },
notifications: []
},
getters: {

View file

@ -53,7 +53,10 @@ export default {
name: "CallBook",
components: { Flipbook },
created() {
this.callManager.on(ECallEvents.ACTION_BOOK_FLIP, this.onRemoteFlip);
this.callManager.on(
ECallEvents.ACTION_BOOK_FLIP,
this.onRemoteFlip.bind(this)
);
},
data() {
return {
@ -66,7 +69,7 @@ export default {
},
methods: {
onFlip(direction: "left" | "right") {
if (this.isHost)
if (this.callManager.isHost)
this.callManager.send(`book:action:flip-page`, { direction });
},
onLeftClicked() {
@ -88,7 +91,9 @@ export default {
createPages(book) {
const pages = [null];
for (let i = 1; i < book.pages + 1; i++) {
pages.push(`/u/books/${book.id}/page/${i}`);
pages.push(
`/u/call/${this.callManager.callId}/books/${book.id}/page/${i}`
);
}
return pages;
}

View file

@ -9,12 +9,12 @@
class="book-thumb m-l-md"
v-for="(book, index) in callManager.books"
:key="index"
@click="goToBook(book, index)"
@click="goToBook(book, index, false)"
:disabled="!callManager.isHost"
>
<div class="book-cover">
<figure class="image is-2by3 m-a">
<img :src="`/u/books/${book.id}/page/1`" />
<img :src="`/u/books/${book.id}/thumbnail`" />
</figure>
</div>
<div class="book-text">
@ -28,12 +28,30 @@
<script lang="ts">
import { mapGetters } from "vuex";
import { ECallEvents } from "../../classes/call.manager";
export default {
name: "CallLobby",
created() {
this.callManager.on(
ECallEvents.CALL_VIEW_BOOK,
this.remoteBookSelected.bind(this)
);
},
methods: {
goToBook(book, index) {
this.callManager.selectBook(index);
this.$router.replace({ name: "book" });
goToBook(book, index, remote) {
if (remote || this.callManager.isHost) {
this.callManager.selectBook(index, remote);
this.$router.replace({ name: "book" });
}
},
remoteBookSelected({ bookId }) {
for (let index = 0; index < this.callManager.books.length; index++) {
const book = this.callManager.books[index];
if (book.id === bookId) {
this.goToBook(book, index, true);
return;
}
}
}
},
computed: {

View file

@ -72,41 +72,19 @@
</div>
</div>
</nav>
<div class="Games">
<h2 class="subtitle">
<i class="fa fa-fw fa-puzzle-piece"></i> My Games
</h2>
<div class="is-flex m-b-md">
<div class="game m-l-md" v-for="i of [1, 2, 3, 4]" :key="i">
<div class="game-cover">
<figure class="image is-2by3 m-a">
<img
src="https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fblog.springshare.com%2Fwp-content%2Fuploads%2F2010%2F02%2Fnc-md.gif&f=1&nofb=1"
/>
</figure>
</div>
<div class="game-text">
<div>Name</div>
<div>Type</div>
</div>
</div>
</div>
</div>
<div class="Books">
<h2 class="subtitle">
<i class="fa fa-fw fa-book"></i> My Books
</h2>
<div class="is-flex m-b-md">
<div class="book-thumb m-l-md" v-for="i of [1, 2, 3, 4]" :key="i">
<div class="book-thumb m-l-md" v-for="book in user.books" :key="book.id">
<div class="book-cover">
<figure class="image is-2by3 m-a">
<img
src="https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fwww.sylviaday.com%2FWP%2Fwp-content%2Fthemes%2Fsylviaday%2Fimages%2Fcovers%2Ftemp-covers%2Fplaceholder-cover.jpg&f=1&nofb=1"
/>
<img :src="`/u/books/${book.id}/thumbnail`" />
</figure>
</div>
<div class="book-text">
<div>book_name</div>
<div>{{book.title}}</div>
</div>
</div>
</div>

View file

@ -48,15 +48,16 @@
</router-link>
</p>
<p class="control">
<router-link
:to="{path:'..'}"
<button
class="button is-info"
append
replace
v-if="inCall && $route.name==='book'"
:disabled="!callManager.isHost"
@click="backToLobby(true)"
>
<i class="fa fa-fw fa-arrow-circle-o-left"></i> Back
</router-link>
</button>
</p>
</div>
</div>
@ -88,7 +89,10 @@
<script lang="ts">
import { mapGetters, mapActions } from "vuex";
import { ECallEvents } from "../../home/classes/call.manager";
export default {
name: "TobNavbar",
created() {},
data() {
return {
showMenu: false
@ -97,6 +101,10 @@ export default {
computed: {
host() {
if (this.inCall) {
this.callManager.on(
ECallEvents.CALL_VIEW_LOBBY,
this.remoteBackToLobby.bind(this)
);
if (this.callManager.isHost) return this.user;
return this.callManager.peer;
}
@ -108,6 +116,13 @@ export default {
changeHost() {
this.callManager.changeHost();
},
backToLobby(payload) {
this.callManager.backToLobby();
this.$router.replace({ path: `/call/${this.callManager.callId}` });
},
remoteBackToLobby(payload) {
this.$router.replace({ path: `/call/${this.callManager.callId}` });
},
...mapActions([])
}
};

View file

@ -42,7 +42,9 @@ const globalMiddleware =
auth: 'Adonis/Middleware/Auth',
guest: 'Adonis/Middleware/AllowGuestOnly',
adminAuth: 'App/Middleware/AdminAuth',
BookPageAuth: 'App/Middleware/BookPageAuth'
BookPageAuth: 'App/Middleware/BookPageAuth',
BookContext: 'App/Middleware/BookContext',
CallContext: 'App/Middleware/CallContext',
}
/*

View file

@ -47,9 +47,16 @@ Route
/**
* Private book assets
*/
Route.get('/u/books/:bookId/page/:pageNumber', 'BookApiController.getPage')
.middleware(['auth', 'BookPageAuth']);
Route
.get(
'/u/call/:callId/books/:bookId/page/:pageNumber',
'BookApiController.getPage')
.middleware(['auth', 'BookContext', 'CallContext', 'BookPageAuth']);
/**
* Public book thumbnail
*/
Route.get('/u/books/:bookId/thumbnail', 'BookApiController.getThumbnail')
.middleware(['auth', 'BookContext'])
/*
/ Pubic CDN Images
*/