DB changes and namings. mainly to start working on the activity and call/sync. Still WIP
This commit is contained in:
parent
42a7f567c8
commit
04d7211de1
16 changed files with 156 additions and 102 deletions
|
@ -76,8 +76,8 @@ class ClientApiController {
|
|||
}
|
||||
const call = await Call.create({
|
||||
state: 'NEW',
|
||||
user_1: user.id,
|
||||
user_2: body.connection_id,
|
||||
parent_id: user.id,
|
||||
guest_id: body.connection_id,
|
||||
child_id: body.child_id
|
||||
});
|
||||
return {
|
||||
|
|
|
@ -49,11 +49,23 @@ class CallSession {
|
|||
this.callModel = callModel;
|
||||
this.hostId = 2;
|
||||
this.state = callModel.state;
|
||||
this.user_1 = {id: callModel.user_1, socket: null, userModel: null};
|
||||
this.user_2 = {id: callModel.user_2, socket: null, userModel: null};
|
||||
this.sessionState = {page: 'lobby', activity: {type: null, model: null}};
|
||||
this.parent = {
|
||||
id: callModel.parent_id,
|
||||
socket: null,
|
||||
userModel: null,
|
||||
isParent: true
|
||||
};
|
||||
this.guest = {
|
||||
id: callModel.guest_id,
|
||||
socket: null,
|
||||
userModel: null,
|
||||
isParent: false
|
||||
};
|
||||
this.startTime = Date.now();
|
||||
this.heartbeat =
|
||||
setInterval(this.onHeartbeat.bind(this), 1000); // Every second
|
||||
this.userMap = new Map(); // Reference to this.parent/guest by userId;
|
||||
}
|
||||
onHeartbeat() {
|
||||
const now = Date.now();
|
||||
|
@ -69,51 +81,50 @@ class CallSession {
|
|||
return this.endCall();
|
||||
}
|
||||
removeUser(user) {
|
||||
let userIndex = -1;
|
||||
if (this.user_1.id === user.id)
|
||||
userIndex = 1;
|
||||
else if (this.user_2.id === user.id)
|
||||
userIndex = 2;
|
||||
if (userIndex < 0) return false;
|
||||
this[`user_${userIndex}`].userModel = null;
|
||||
this[`user_${userIndex}`].socket = null;
|
||||
let userToRemove = this.userMap.get(user.id);
|
||||
userToRemove.userModel = null;
|
||||
userToRemove.socket = null;
|
||||
this.userMap.delete(user.id);
|
||||
this.updateState();
|
||||
if (this.state === 'ENDED') this.endCall();
|
||||
}
|
||||
async registerUser(user, socket) {
|
||||
if (!this.child) this.child = await Child.find(this.callModel.child_id);
|
||||
let userIndex = -1;
|
||||
if (this.user_1.id === user.id)
|
||||
userIndex = 1;
|
||||
else if (this.user_2.id === user.id)
|
||||
userIndex = 2;
|
||||
if (userIndex < 0) return false;
|
||||
const otherUser = userIndex === 1 ? 2 : 1;
|
||||
this[`user_${userIndex}`].userModel = user;
|
||||
this[`user_${userIndex}`].socket = socket;
|
||||
if (!this.child) this.child = await this.callModel.child().fetch();
|
||||
let isParent = this.parent.id === user.id;
|
||||
let peerId = isParent ? this.guest.id : this.parent.id;
|
||||
if (isParent) {
|
||||
this.parent.userModel = user;
|
||||
this.parent.socket = socket;
|
||||
this.userMap.set(user.id, this.parent);
|
||||
} else {
|
||||
this.guest.userModel = user;
|
||||
this.guest.socket = socket;
|
||||
this.userMap.set(user.id, this.guest);
|
||||
peerId = this.parent.id;
|
||||
}
|
||||
|
||||
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));
|
||||
socket.on('call:host:changed', this.onHostChanged.bind(this));
|
||||
socket
|
||||
.on('book:action:flip-page', this.onActionBookFlip.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);
|
||||
await this.sendStandby(socket, user.id, peerId);
|
||||
// Send event to other user about the call
|
||||
console.log(
|
||||
`trying to find user ${this[`user_${otherUser}`].id} channel...`);
|
||||
const otherUserChannel =
|
||||
UserChannel.getUserChannel(this[`user_${otherUser}`].id);
|
||||
console.log(`trying to find peer's ${peerId} channel...`);
|
||||
const otherUserChannel = UserChannel.getUserChannel(peerId);
|
||||
if (otherUserChannel) {
|
||||
// console.log(otherUserChannel);
|
||||
console.log('Sending notification to other user');
|
||||
console.log(`Sending notification to peer ${peerId}`);
|
||||
const payload = {callId: this.callId, child: this.child.toJSON()};
|
||||
console.dir(payload);
|
||||
otherUserChannel.emit('call:incoming', payload);
|
||||
}
|
||||
} else if (this.state === 'IN_PROGRESS')
|
||||
await this.sendStart(socket, userIndex);
|
||||
} else if (this.state === 'IN_PROGRESS') {
|
||||
await this.sendStart(socket, user.id, peerId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
endCall() {
|
||||
|
@ -122,31 +133,34 @@ class CallSession {
|
|||
this.callModel.state = this.state;
|
||||
this.callModel.save();
|
||||
}
|
||||
if (this.user_1.socket) this.user_1.socket.close();
|
||||
if (this.user_2.socket) this.user_2.socket.close();
|
||||
if (this.parent.socket) this.parent.socket.close();
|
||||
if (this.guest.socket) this.guest.socket.close();
|
||||
clearInterval(this.heartbeat);
|
||||
this.onCallEndedCallback(this.callId);
|
||||
}
|
||||
|
||||
async sendStandby(socket, userIndex) {
|
||||
console.log(`Call #${this.callId} sendStandby -> ${userIndex}`);
|
||||
async sendStandby(socket, userId, peerId) {
|
||||
console.log(`Call #${this.callId} sendStandby -> ${userId}`);
|
||||
const iceServers = (await IceServer.all()).rows.map(i => i.toJSON());
|
||||
console.log(await this.callModel.parent().fetch());
|
||||
socket.emit('call:standby', {
|
||||
iceServers,
|
||||
id: userIndex,
|
||||
peerId,
|
||||
child: this.child.toJSON(),
|
||||
users: await this.callModel.getUsers(),
|
||||
users: await Promise.all(
|
||||
[this.callModel.parent().fetch(), this.callModel.guest().fetch()]),
|
||||
hostId: this.hostId
|
||||
});
|
||||
}
|
||||
async sendStart(socket, userIndex) {
|
||||
console.log(`Call #${this.callId} sendStart -> ${userIndex}`);
|
||||
async sendStart(socket, userId, peerId) {
|
||||
console.log(`Call #${this.callId} sendStart -> ${userId}`);
|
||||
const iceServers = (await IceServer.all()).rows.map(i => i.toJSON());
|
||||
socket.emit('call:start', {
|
||||
iceServers,
|
||||
id: userIndex,
|
||||
peerId,
|
||||
child: this.child.toJSON(),
|
||||
users: await this.callModel.getUsers(),
|
||||
users: await Promise.all(
|
||||
[this.callModel.parent().fetch(), this.callModel.guest().fetch()]),
|
||||
hostId: this.hostId
|
||||
});
|
||||
}
|
||||
|
@ -171,44 +185,39 @@ class CallSession {
|
|||
console.log(`Call #${this.callId} state=${this.state}`);
|
||||
}
|
||||
areAllPartnersConnected() {
|
||||
return !!this.user_1.socket && !!this.user_2.socket;
|
||||
return !!this.parent.socket && !!this.guest.socket;
|
||||
}
|
||||
async onIceCandidate(payload) {
|
||||
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}`);
|
||||
const {peerId, userId, ice} = payload;
|
||||
this.userMap.get(peerId).socket.emit('wrtc:ice', {ice});
|
||||
console.log(`[Signal] [onIceCandidate] ${userId} -> ${peerId}`);
|
||||
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}`);
|
||||
const {peerId, userId, sdp} = payload;
|
||||
this.userMap.get(peerId).socket.emit('wrtc:sdp:offer', {sdp});
|
||||
console.log(`[Signal] [onSdpOffer] ${userId} -> ${peerId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
async onSdpAnswer(payload) {
|
||||
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}`);
|
||||
const {peerId, userId, sdp} = payload;
|
||||
this.userMap.get(peerId).socket.emit('wrtc:sdp:answer', {sdp});
|
||||
console.log(`[Signal] [onSdpAnswer] ${userId} -> ${peerId}`);
|
||||
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}`);
|
||||
const {peerId, userId, direction} = payload;
|
||||
this.userMap.get(peerId).socket.emit('book:action:flip-page', {direction});
|
||||
console.log(`[Signal] [book] [action] [flip] [${direction}] ${userId} -> ${
|
||||
peerId}`);
|
||||
return true;
|
||||
}
|
||||
async onHostChanged(payload) {
|
||||
const {from = payload.id, hostId} = payload;
|
||||
const to = from === 1 ? 2 : 1;
|
||||
const {peerId, hostId} = payload;
|
||||
this.hostId = hostId;
|
||||
this[`user_${to}`].socket.emit('call:host:changed', {hostId});
|
||||
this.userMap.get(peerId).socket.emit('call:host:changed', {hostId});
|
||||
console.log(
|
||||
`[Signal] [host] [changed] [hostId=${hostId}] ${from} -> ${to}`);
|
||||
return true;
|
||||
|
|
|
@ -20,7 +20,7 @@ class WsCallAuth {
|
|||
throw new Error('Call not found');
|
||||
}
|
||||
if (call.state === 'ENDED') throw new Error('This call has ended');
|
||||
if (user.id === call.user_1 || user.id === call.user_2) {
|
||||
if (user.id === call.parent_id || user.id === call.guest_id) {
|
||||
ctx.call = call;
|
||||
await next()
|
||||
}
|
||||
|
|
20
app/Models/ActivityLog.js
Normal file
20
app/Models/ActivityLog.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
|
||||
const Model = use('Model')
|
||||
|
||||
class ActivityLog extends Model {
|
||||
async call() {
|
||||
return this.belongsTo('App/Models/Call');
|
||||
}
|
||||
async resource() {
|
||||
switch (this.type) {
|
||||
case 'BOOK':
|
||||
return this.belongsTo('App/Models/Book');
|
||||
default:
|
||||
throw new Error('Unknown Resource Type');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ActivityLog
|
|
@ -5,6 +5,7 @@ const Model = use('Model')
|
|||
|
||||
class Book extends Model {
|
||||
user() {
|
||||
if (!this.user_id) return null;
|
||||
return this.belongsTo('App/Models/User');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,14 @@ const Model = use('Model')
|
|||
const User = use('App/Models/User');
|
||||
|
||||
class Call extends Model {
|
||||
async getUsers() {
|
||||
const users = [
|
||||
(await User.find(this.user_1)).toJSON(),
|
||||
(await User.find(this.user_2)).toJSON()
|
||||
];
|
||||
|
||||
return users;
|
||||
parent() {
|
||||
return this.belongsTo('App/Models/User', 'parent_id');
|
||||
}
|
||||
guest() {
|
||||
return this.belongsTo('App/Models/User', 'guest_id');
|
||||
}
|
||||
child() {
|
||||
return this.belongsTo('App/Models/Child');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,13 @@ class CallSchema extends Schema {
|
|||
this.create('calls', (table) => {
|
||||
table.increments();
|
||||
table.string('state').notNullable();
|
||||
table.bigInteger('user_1').notNullable();
|
||||
table.bigInteger('user_2').notNullable();
|
||||
table.bigInteger('parent_id').notNullable();
|
||||
table.bigInteger('guest_id').notNullable();
|
||||
table.bigInteger('child_id').notNullable();
|
||||
table.timestamps();
|
||||
table.index(['child_id']);
|
||||
table.index(['user_1']);
|
||||
table.index(['user_2']);
|
||||
table.index(['parent_id']);
|
||||
table.index(['guest_id']);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@ class BookSchema extends Schema {
|
|||
up() {
|
||||
this.create('books', (table) => {
|
||||
table.increments()
|
||||
table.bigInteger('user_id').notNullable();
|
||||
table.bigInteger('user_id');
|
||||
table.integer('pages').notNullable();
|
||||
table.string('book_folder').notNullable();
|
||||
table.boolean('ltr').default(true);
|
||||
table.timestamps()
|
||||
})
|
||||
}
|
||||
|
|
22
database/migrations/1587931580121_activity_log_schema.js
Normal file
22
database/migrations/1587931580121_activity_log_schema.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
'use strict'
|
||||
|
||||
/** @type {import('@adonisjs/lucid/src/Schema')} */
|
||||
const Schema = use('Schema')
|
||||
|
||||
class ActivityLogSchema extends Schema {
|
||||
up() {
|
||||
this.create('activity_logs', (table) => {
|
||||
table.increments();
|
||||
table.bigInteger('call_id').notNullable();
|
||||
table.string('type', 60).notNullable();
|
||||
table.bigInteger('resource_id').notNullable();
|
||||
table.timestamps(); // created_at = start time, updated_at = end time
|
||||
});
|
||||
}
|
||||
|
||||
down() {
|
||||
this.drop('activity_logs');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ActivityLogSchema
|
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
|
@ -10,11 +10,11 @@ export default class CallManager {
|
|||
private emitter = new EventEmitter();
|
||||
private pc: RTCPeerConnection;
|
||||
public child;
|
||||
public guest = { avatar: '' };
|
||||
public peer = { avatar: '' };
|
||||
public isHost = false;
|
||||
constructor(private ws: WebSocketService, private callId: number, private userId: number) {
|
||||
this.inCall = false;
|
||||
this.peerId = -1;
|
||||
this.peerId = null;
|
||||
this.pc = null;
|
||||
this.remoteStream = new MediaStream();
|
||||
}
|
||||
|
@ -55,19 +55,20 @@ export default class CallManager {
|
|||
}
|
||||
private send(event: string, payload: { [key: string]: any }) {
|
||||
this.signalingChannel.emit(event, {
|
||||
id: this.peerId,
|
||||
userId: this.userId,
|
||||
peerId: this.peerId,
|
||||
...payload
|
||||
})
|
||||
}
|
||||
async onCallStart(payload: { iceServers: RTCIceServer[], id: number, users: any[], child: any, hostId: number }) {
|
||||
async onCallStart(payload: { iceServers: RTCIceServer[], peerId: number, users: any[], child: any, hostId: number }) {
|
||||
console.log('onCallStart');
|
||||
console.log(payload);
|
||||
this.peerId = payload.id;
|
||||
this.peerId = payload.peerId;
|
||||
this.isHost = this.peerId === payload.hostId;
|
||||
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
|
||||
this.child = payload.child;
|
||||
payload.users.forEach(u => {
|
||||
if (u.id !== this.userId) this.guest = u;
|
||||
if (u.id === this.peerId) this.peer = u;
|
||||
});
|
||||
this.emit(ECallEvents.CALL_HOST_CHANGED, { hostId: payload.hostId });
|
||||
console.log('Created PeerConnection');
|
||||
|
@ -83,15 +84,15 @@ export default class CallManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
async onCallStandby(payload: { iceServers: RTCIceServer[], id: number, users: any[], child: any, hostId: number }) {
|
||||
async onCallStandby(payload: { iceServers: RTCIceServer[], peerId: number, users: any[], child: any, hostId: number }) {
|
||||
console.log('onCallStandby');
|
||||
console.log(payload);
|
||||
this.peerId = payload.id;
|
||||
this.peerId = payload.peerId;
|
||||
this.isHost = this.peerId === payload.hostId;
|
||||
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
|
||||
this.child = payload.child;
|
||||
payload.users.forEach(u => {
|
||||
if (u.id !== this.userId) this.guest = u;
|
||||
if (u.id === this.peerId) this.peer = u;
|
||||
});
|
||||
this.emit(ECallEvents.CALL_HOST_CHANGED, { hostId: payload.hostId });
|
||||
console.log('Created PeerConnection');
|
||||
|
@ -174,8 +175,7 @@ export default class CallManager {
|
|||
|
||||
changeHost() {
|
||||
this.isHost = !this.isHost;
|
||||
const guestPeerId = this.peerId === 1 ? 2 : 1;
|
||||
const hostId = this.isHost ? this.peerId : guestPeerId;
|
||||
const hostId = this.isHost ? this.userId : this.peerId;
|
||||
this.emit(ECallEvents.CALL_HOST_CHANGED, { hostId });
|
||||
this.send('call:host:changed', { hostId });
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="me">
|
||||
<figure class="image is-24x24">
|
||||
<img
|
||||
:src="isHost? user.avatar : callManager.guest.avatar"
|
||||
:src="isHost? user.avatar : callManager.peer.avatar"
|
||||
class="is-rounded is-avatar"
|
||||
@click="changeHost()"
|
||||
/>
|
||||
|
@ -150,7 +150,7 @@ export default {
|
|||
onRemoteHostChanged(payload) {
|
||||
console.log("-----------");
|
||||
console.log(payload);
|
||||
this.guest = this.callManager.guest;
|
||||
this.peer = this.callManager.peer;
|
||||
this.isHost = this.callManager.isHost;
|
||||
},
|
||||
changeHost() {
|
||||
|
|
|
@ -44,7 +44,7 @@ Seepur | Our Time together
|
|||
<p>
|
||||
With Seepur you can simply choose your grandchild/niece/nephew you wish to connect with, click on your chosen activity for the day, and share a fun experience together.
|
||||
</p>
|
||||
<a href="#" class="button m-t-md">More...</a>
|
||||
{{-- <a href="#" class="button m-t-md">More...</a> --}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
|
@ -108,7 +108,7 @@ Seepur | Our Time together
|
|||
Make an actual impact on the child by creating shared memories. You will be surprised how engaged and comunicative a little child could be during active sessions.
|
||||
With the great combination of video calling and your shared view of the book pages, you can make the story come alive by using different voices for different characters, and even acting out parts of the story, as if you’re right there by their side.
|
||||
</p>
|
||||
<a href="#" class="button m-t-md">More...</a>
|
||||
{{-- <a href="#" class="button m-t-md">More...</a> --}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -120,7 +120,7 @@ Seepur | Our Time together
|
|||
<p>
|
||||
Seepur is all about you and the child. You can upload your own books and read together, create puzzles with your own photos and more!
|
||||
</p>
|
||||
<a href="#" class="button m-t-md">More...</a>
|
||||
{{-- <a href="#" class="button m-t-md">More...</a> --}}
|
||||
</div>
|
||||
<div class="column">
|
||||
<figure class="image is-5by4">
|
||||
|
|
Reference in a new issue