diff --git a/app/Controllers/Http/ClientApiController.js b/app/Controllers/Http/ClientApiController.js
index 3c7f2ff..97eb8b7 100644
--- a/app/Controllers/Http/ClientApiController.js
+++ b/app/Controllers/Http/ClientApiController.js
@@ -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 {
diff --git a/app/Controllers/Ws/SignalingController.js b/app/Controllers/Ws/SignalingController.js
index 5e0a568..d72de67 100644
--- a/app/Controllers/Ws/SignalingController.js
+++ b/app/Controllers/Ws/SignalingController.js
@@ -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;
diff --git a/app/Middleware/WsCallAuth.js b/app/Middleware/WsCallAuth.js
index 54c45c2..d208a93 100644
--- a/app/Middleware/WsCallAuth.js
+++ b/app/Middleware/WsCallAuth.js
@@ -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()
}
diff --git a/app/Models/ActivityLog.js b/app/Models/ActivityLog.js
new file mode 100644
index 0000000..44b112b
--- /dev/null
+++ b/app/Models/ActivityLog.js
@@ -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
diff --git a/app/Models/Book.js b/app/Models/Book.js
index 21ba4c7..47c85ac 100644
--- a/app/Models/Book.js
+++ b/app/Models/Book.js
@@ -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');
}
}
diff --git a/app/Models/Call.js b/app/Models/Call.js
index 6c233fe..5ff7d87 100644
--- a/app/Models/Call.js
+++ b/app/Models/Call.js
@@ -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');
}
}
diff --git a/database/migrations/1586374249535_call_schema.js b/database/migrations/1586374249535_call_schema.js
index 6ccbfc5..d16a4b2 100644
--- a/database/migrations/1586374249535_call_schema.js
+++ b/database/migrations/1586374249535_call_schema.js
@@ -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']);
})
}
diff --git a/database/migrations/1586821478599_book_schema.js b/database/migrations/1586821478599_book_schema.js
index be40dda..b92dd78 100644
--- a/database/migrations/1586821478599_book_schema.js
+++ b/database/migrations/1586821478599_book_schema.js
@@ -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()
})
}
diff --git a/database/migrations/1587931580121_activity_log_schema.js b/database/migrations/1587931580121_activity_log_schema.js
new file mode 100644
index 0000000..164ced0
--- /dev/null
+++ b/database/migrations/1587931580121_activity_log_schema.js
@@ -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
diff --git a/public/scripts/applications/admin/app.bundle.js b/public/scripts/applications/admin/app.bundle.js
index 6faf0a0..55a2097 100644
--- a/public/scripts/applications/admin/app.bundle.js
+++ b/public/scripts/applications/admin/app.bundle.js
@@ -1,4 +1,4 @@
-!function(e){var t=window.webpackHotUpdate;window.webpackHotUpdate=function(e,n){!function(e,t){if(!b[e]||!_[e])return;for(var n in _[e]=!1,t)Object.prototype.hasOwnProperty.call(t,n)&&(v[n]=t[n]);0==--m&&0===y&&$()}(e,n),t&&t(e,n)};var n,r=!0,i="bac60fe172c67ad1f479",o={},a=[],s=[];function c(e){var t=O[e];if(!t)return A;var r=function(r){return t.hot.active?(O[r]?-1===O[r].parents.indexOf(e)&&O[r].parents.push(e):(a=[e],n=r),-1===t.children.indexOf(r)&&t.children.push(r)):(console.warn("[HMR] unexpected require("+r+") from disposed module "+e),a=[]),A(r)},i=function(e){return{configurable:!0,enumerable:!0,get:function(){return A[e]},set:function(t){A[e]=t}}};for(var o in A)Object.prototype.hasOwnProperty.call(A,o)&&"e"!==o&&"t"!==o&&Object.defineProperty(r,o,i(o));return r.e=function(e){return"ready"===p&&f("prepare"),y++,A.e(e).then(t,(function(e){throw t(),e}));function t(){y--,"prepare"===p&&(g[e]||C(e),0===y&&0===m&&$())}},r.t=function(e,t){return 1&t&&(e=r(e)),A.t(e,-2&t)},r}function u(e){var t={_acceptedDependencies:{},_declinedDependencies:{},_selfAccepted:!1,_selfDeclined:!1,_disposeHandlers:[],_main:n!==e,active:!0,accept:function(e,n){if(void 0===e)t._selfAccepted=!0;else if("function"==typeof e)t._selfAccepted=e;else if("object"==typeof e)for(var r=0;rdocument.createEvent("Event").timeStamp&&(ln=function(){return cn.now()})}function mn(){var e,t;for(un=ln(),on=!0,nn.sort((function(e,t){return e.id-t.id})),dn=0;dn-1?vr(e,t,n):Rn(t)?Nn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):En(t)?e.setAttribute(t,function(e,t){return Nn(t)||"false"===t?"false":"contenteditable"===e&&Fn(t)?t:"true"}(t,n)):Wn(t)?Nn(n)?e.removeAttributeNS($n,In(t)):e.setAttributeNS($n,t,n):vr(e,t,n)}function vr(e,t,n){if(Nn(n))e.removeAttribute(t);else{if(Z&&!X&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var yr={create:pr,update:pr};function gr(e,t){var n=t.elm,r=t.data,o=e.data;if(!(s(r.staticClass)&&s(r.class)&&(s(o)||s(o.staticClass)&&s(o.class)))){var i=zn(t),d=n._transitionClasses;a(d)&&(i=Un(i,Gn(d))),i!==n._prevClass&&(n.setAttribute("class",i),n._prevClass=i)}}var Mr,Lr,Yr,kr,wr,br,Dr={create:gr,update:gr},Tr=/[\w).+\-_$\]]/;function jr(e){var t,n,r,s,a,o=!1,i=!1,d=!1,u=!1,l=0,c=0,m=0,_=0;for(r=0;rdocument.createEvent("Event").timeStamp&&(ln=function(){return cn.now()})}function mn(){var e,t;for(un=ln(),on=!0,nn.sort((function(e,t){return e.id-t.id})),dn=0;dn-1?vr(e,t,n):Rn(t)?Nn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):En(t)?e.setAttribute(t,function(e,t){return Nn(t)||"false"===t?"false":"contenteditable"===e&&Fn(t)?t:"true"}(t,n)):In(t)?Nn(n)?e.removeAttributeNS($n,Wn(t)):e.setAttributeNS($n,t,n):vr(e,t,n)}function vr(e,t,n){if(Nn(n))e.removeAttribute(t);else{if(Z&&!X&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var yr={create:pr,update:pr};function Mr(e,t){var n=t.elm,r=t.data,o=e.data;if(!(s(r.staticClass)&&s(r.class)&&(s(o)||s(o.staticClass)&&s(o.class)))){var i=zn(t),d=n._transitionClasses;a(d)&&(i=Un(i,Gn(d))),i!==n._prevClass&&(n.setAttribute("class",i),n._prevClass=i)}}var gr,Lr,Yr,kr,wr,br,Dr={create:Mr,update:Mr},Tr=/[\w).+\-_$\]]/;function jr(e){var t,n,r,s,a,o=!1,i=!1,d=!1,u=!1,l=0,c=0,m=0,_=0;for(r=0;r
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!
- More... + {{-- More... --}}