Fixed call. Added lobby signals + book selection signals
This commit is contained in:
parent
0e2fa21511
commit
cef526205c
18 changed files with 192 additions and 67 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
27
app/Middleware/BookContext.js
Normal file
27
app/Middleware/BookContext.js
Normal 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
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
27
app/Middleware/CallContext.js
Normal file
27
app/Middleware/CallContext.js
Normal 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
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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([])
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
Reference in a new issue