forked from sagi/seepur
[WIP] Basic lobby and multibook support. Call is not working ATM. Need to fix signaling
This commit is contained in:
parent
04d7211de1
commit
0e2fa21511
35 changed files with 634 additions and 257 deletions
17
app/Controllers/Http/BookApiController.js
Normal file
17
app/Controllers/Http/BookApiController.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
'use strict'
|
||||||
|
const FileUtils = use('App/Utils/FileUtils');
|
||||||
|
class BookApiController {
|
||||||
|
async getPage({request, book, response}) {
|
||||||
|
const pageNumber = Number(request.params.pageNumber);
|
||||||
|
const file =
|
||||||
|
await FileUtils.getFile(`books/${book.book_folder}/${pageNumber}.jpg`);
|
||||||
|
if (file)
|
||||||
|
response.send(file);
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
code: 404, message: 'no file'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BookApiController
|
|
@ -8,7 +8,7 @@ class CdnController {
|
||||||
response.send(file);
|
response.send(file);
|
||||||
else
|
else
|
||||||
return {
|
return {
|
||||||
ERROR: 'no file'
|
code: 404, message: 'no file'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
const User = use('App/Models/User');
|
const User = use('App/Models/User');
|
||||||
const UserChildUtils = use('App/Utils/UserChildUtils');
|
const UserChildUtils = use('App/Utils/UserChildUtils');
|
||||||
|
const CallUtils = use('App/Utils/CallUtils');
|
||||||
const Child = use('App/Models/Child');
|
const Child = use('App/Models/Child');
|
||||||
const IceServer = use('App/Models/IceServer');
|
const IceServer = use('App/Models/IceServer');
|
||||||
const UserChannel = use('App/Controllers/Ws/UserChannelController')
|
const UserChannel = use('App/Controllers/Ws/UserChannelController')
|
||||||
|
@ -47,6 +48,7 @@ class CallSession {
|
||||||
this.onCallEndedCallback = onCallEndedCallback;
|
this.onCallEndedCallback = onCallEndedCallback;
|
||||||
this.callId = callModel.id;
|
this.callId = callModel.id;
|
||||||
this.callModel = callModel;
|
this.callModel = callModel;
|
||||||
|
this.callBooks = null;
|
||||||
this.hostId = 2;
|
this.hostId = 2;
|
||||||
this.state = callModel.state;
|
this.state = callModel.state;
|
||||||
this.sessionState = {page: 'lobby', activity: {type: null, model: null}};
|
this.sessionState = {page: 'lobby', activity: {type: null, model: null}};
|
||||||
|
@ -90,6 +92,9 @@ class CallSession {
|
||||||
}
|
}
|
||||||
async registerUser(user, socket) {
|
async registerUser(user, socket) {
|
||||||
if (!this.child) this.child = await this.callModel.child().fetch();
|
if (!this.child) this.child = await this.callModel.child().fetch();
|
||||||
|
if (!this.callBooks)
|
||||||
|
this.callBooks = await CallUtils.getBooks(
|
||||||
|
this.callModel.parent_id, this.callModel.guest_id);
|
||||||
let isParent = this.parent.id === user.id;
|
let isParent = this.parent.id === user.id;
|
||||||
let peerId = isParent ? this.guest.id : this.parent.id;
|
let peerId = isParent ? this.guest.id : this.parent.id;
|
||||||
if (isParent) {
|
if (isParent) {
|
||||||
|
@ -146,6 +151,7 @@ class CallSession {
|
||||||
socket.emit('call:standby', {
|
socket.emit('call:standby', {
|
||||||
iceServers,
|
iceServers,
|
||||||
peerId,
|
peerId,
|
||||||
|
books: this.callBooks,
|
||||||
child: this.child.toJSON(),
|
child: this.child.toJSON(),
|
||||||
users: await Promise.all(
|
users: await Promise.all(
|
||||||
[this.callModel.parent().fetch(), this.callModel.guest().fetch()]),
|
[this.callModel.parent().fetch(), this.callModel.guest().fetch()]),
|
||||||
|
@ -158,6 +164,7 @@ class CallSession {
|
||||||
socket.emit('call:start', {
|
socket.emit('call:start', {
|
||||||
iceServers,
|
iceServers,
|
||||||
peerId,
|
peerId,
|
||||||
|
books: this.callBooks,
|
||||||
child: this.child.toJSON(),
|
child: this.child.toJSON(),
|
||||||
users: await Promise.all(
|
users: await Promise.all(
|
||||||
[this.callModel.parent().fetch(), this.callModel.guest().fetch()]),
|
[this.callModel.parent().fetch(), this.callModel.guest().fetch()]),
|
||||||
|
@ -215,11 +222,16 @@ class CallSession {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
async onHostChanged(payload) {
|
async onHostChanged(payload) {
|
||||||
const {peerId, hostId} = payload;
|
const {peerId, userId} = payload;
|
||||||
this.hostId = hostId;
|
this.hostId = this.hostId === userId ? peerId : userId;
|
||||||
this.userMap.get(peerId).socket.emit('call:host:changed', {hostId});
|
console.log('Host: ', this.hostId);
|
||||||
console.log(
|
this.userMap.get(userId).socket.emit(
|
||||||
`[Signal] [host] [changed] [hostId=${hostId}] ${from} -> ${to}`);
|
'call:host:changed', {hostId: this.hostId});
|
||||||
|
try {
|
||||||
|
this.userMap.get(peerId).socket.emit(
|
||||||
|
'call:host:changed', {hostId: this.hostId});
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
40
app/Middleware/BookPageAuth.js
Normal file
40
app/Middleware/BookPageAuth.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
'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');
|
||||||
|
const UserChildUtils = use('App/Utils/UserChildUtils');
|
||||||
|
class BookPageAuth {
|
||||||
|
/**
|
||||||
|
* @param {object} ctx
|
||||||
|
* @param {Request} ctx.request
|
||||||
|
* @param {Function} next
|
||||||
|
*/
|
||||||
|
async handle(ctx, next) {
|
||||||
|
const {request, auth, response} = 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)
|
||||||
|
await next();
|
||||||
|
else {
|
||||||
|
const ownerConnections =
|
||||||
|
await UserChildUtils.getUserConnections(book.user_id);
|
||||||
|
console.log(ownerConnections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BookPageAuth
|
|
@ -4,6 +4,9 @@
|
||||||
const Model = use('Model')
|
const Model = use('Model')
|
||||||
|
|
||||||
class Book extends Model {
|
class Book extends Model {
|
||||||
|
static get hidden() {
|
||||||
|
return ['user_id'];
|
||||||
|
}
|
||||||
user() {
|
user() {
|
||||||
if (!this.user_id) return null;
|
if (!this.user_id) return null;
|
||||||
return this.belongsTo('App/Models/User');
|
return this.belongsTo('App/Models/User');
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
|
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
|
||||||
const Model = use('Model')
|
const Model = use('Model')
|
||||||
const User = use('App/Models/User');
|
const User = use('App/Models/User');
|
||||||
|
const Book = use('App/Models/Book');
|
||||||
|
|
||||||
class Call extends Model {
|
class Call extends Model {
|
||||||
parent() {
|
parent() {
|
||||||
|
|
18
app/Utils/CallUtils.js
Normal file
18
app/Utils/CallUtils.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
|
||||||
|
const Book = use('App/Models/Book');
|
||||||
|
class CallUtils {
|
||||||
|
static async getBooks(parent_id, guest_id) {
|
||||||
|
const result = await Book.query()
|
||||||
|
.where({user_id: parent_id})
|
||||||
|
.orWhere({user_id: guest_id})
|
||||||
|
.orWhere({user_id: null})
|
||||||
|
.fetch();
|
||||||
|
if (!result.rows.length)
|
||||||
|
return [];
|
||||||
|
else
|
||||||
|
return result.rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CallUtils;
|
|
@ -15,6 +15,7 @@ class FileUtils {
|
||||||
try {
|
try {
|
||||||
return await Drive.get(filename)
|
return await Drive.get(filename)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ class BookSchema extends Schema {
|
||||||
this.create('books', (table) => {
|
this.create('books', (table) => {
|
||||||
table.increments()
|
table.increments()
|
||||||
table.bigInteger('user_id');
|
table.bigInteger('user_id');
|
||||||
|
table.string('title', 80).notNullable();
|
||||||
table.integer('pages').notNullable();
|
table.integer('pages').notNullable();
|
||||||
table.string('book_folder').notNullable();
|
table.string('book_folder').notNullable();
|
||||||
table.boolean('ltr').default(true);
|
table.boolean('ltr').default(true);
|
||||||
|
|
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
|
@ -6541,10 +6541,24 @@ label.panel-block {
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
padding: 3rem 1.5rem 6rem; }
|
padding: 3rem 1.5rem 6rem; }
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background: #fafafa; }
|
||||||
|
|
||||||
.hero-bg {
|
.hero-bg {
|
||||||
background: repeating-linear-gradient(-45deg, transparent, transparent 1em, rgba(127, 215, 245, 0.4) 0, rgba(127, 215, 245, 0.1) 2em, transparent 0, transparent 1em, rgba(127, 215, 245, 0.3) 0, rgba(127, 215, 245, 0.2) 4em, transparent 0, transparent 1em, rgba(192, 235, 250, 0.6) 0, rgba(192, 235, 250, 0.2) 2em), repeating-linear-gradient(45deg, transparent, transparent 1em, rgba(127, 215, 245, 0.4) 0, rgba(127, 215, 245, 0.1) 2em, transparent 0, transparent 1em, rgba(127, 215, 245, 0.3) 0, rgba(127, 215, 245, 0.2) 4em, transparent 0, transparent 1em, rgba(192, 235, 250, 0.4) 0, rgba(192, 235, 250, 0.1) 2em), #FFF;
|
background: repeating-linear-gradient(-45deg, transparent, transparent 1em, rgba(127, 215, 245, 0.4) 0, rgba(127, 215, 245, 0.1) 2em, transparent 0, transparent 1em, rgba(127, 215, 245, 0.3) 0, rgba(127, 215, 245, 0.2) 4em, transparent 0, transparent 1em, rgba(192, 235, 250, 0.6) 0, rgba(192, 235, 250, 0.2) 2em), repeating-linear-gradient(45deg, transparent, transparent 1em, rgba(127, 215, 245, 0.4) 0, rgba(127, 215, 245, 0.1) 2em, transparent 0, transparent 1em, rgba(127, 215, 245, 0.3) 0, rgba(127, 215, 245, 0.2) 4em, transparent 0, transparent 1em, rgba(192, 235, 250, 0.4) 0, rgba(192, 235, 250, 0.1) 2em), #FFF;
|
||||||
background-blend-mode: multiply; }
|
background-blend-mode: multiply; }
|
||||||
|
|
||||||
|
.navbar-menu.is-active {
|
||||||
|
animation: expand-animation .1s ease-in; }
|
||||||
|
|
||||||
|
@keyframes expand-animation {
|
||||||
|
from {
|
||||||
|
height: 0;
|
||||||
|
transform: scaleY(0); }
|
||||||
|
to {
|
||||||
|
transform: scaleY(1);
|
||||||
|
height: auto; } }
|
||||||
|
|
||||||
.hero .navbar.darken {
|
.hero .navbar.darken {
|
||||||
background: rgba(0, 0, 0, 0.7);
|
background: rgba(0, 0, 0, 0.7);
|
||||||
/* Old browsers */
|
/* Old browsers */
|
||||||
|
@ -6846,13 +6860,19 @@ label.panel-block {
|
||||||
.is-fullheight {
|
.is-fullheight {
|
||||||
min-height: calc(100vh - ( 3.25rem ));
|
min-height: calc(100vh - ( 3.25rem ));
|
||||||
max-height: calc(100vh - ( 3.25rem ));
|
max-height: calc(100vh - ( 3.25rem ));
|
||||||
height: calc(100vh - ( 3.25rem ));
|
height: calc(100vh - ( 3.25rem )); }
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: stretch; }
|
|
||||||
.is-fullheight .column {
|
.is-fullheight .column {
|
||||||
overflow-y: auto; }
|
overflow-y: auto; }
|
||||||
|
|
||||||
|
.is-fullwidth {
|
||||||
|
width: 100vw;
|
||||||
|
overflow: hidden; }
|
||||||
|
|
||||||
|
.app-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center; }
|
||||||
|
|
||||||
.avatar-badge-image {
|
.avatar-badge-image {
|
||||||
display: table;
|
display: table;
|
||||||
margin: auto; }
|
margin: auto; }
|
||||||
|
@ -6917,16 +6937,37 @@ label.panel-block {
|
||||||
|
|
||||||
.flipbook {
|
.flipbook {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60vh; }
|
height: 75vh;
|
||||||
|
overflow-y: visible; }
|
||||||
|
|
||||||
|
.book-flip-buttons {
|
||||||
|
height: 100%; }
|
||||||
|
|
||||||
.video-strip {
|
.video-strip {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-content: center; }
|
align-content: center;
|
||||||
|
max-height: 15vh; }
|
||||||
|
|
||||||
video {
|
video {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
background-color: rgba(56, 181, 187, 0.19);
|
background-color: rgba(56, 181, 187, 0.19);
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
border: solid 1px rgba(56, 181, 187, 0.3); }
|
border: solid 1px rgba(56, 181, 187, 0.3);
|
||||||
|
flex-basis: 100%;
|
||||||
|
max-width: 15vh; }
|
||||||
|
|
||||||
|
.book-thumb {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all .2s;
|
||||||
|
z-index: inherit;
|
||||||
|
flex-basis: 12%;
|
||||||
|
text-align: center; }
|
||||||
|
.book-thumb:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
z-index: 10; }
|
||||||
|
|
||||||
|
.book-thumb:disabled:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: scale(1); }
|
||||||
|
|
|
@ -5,10 +5,30 @@
|
||||||
@import './mixins.scss';
|
@import './mixins.scss';
|
||||||
@import "../../node_modules/bulma/bulma.sass";
|
@import "../../node_modules/bulma/bulma.sass";
|
||||||
// @import '../../node_modules/animate.css/source/_base.css';
|
// @import '../../node_modules/animate.css/source/_base.css';
|
||||||
|
.navbar {
|
||||||
|
background:#fafafa;
|
||||||
|
}
|
||||||
.hero-bg{
|
.hero-bg{
|
||||||
@include pattern(#c0ebfa, #7FD7F5);
|
@include pattern(#c0ebfa, #7FD7F5);
|
||||||
}
|
}
|
||||||
|
.navbar-menu{
|
||||||
|
&.is-active{
|
||||||
|
// transition: all;
|
||||||
|
// position: relative;
|
||||||
|
// top:0;
|
||||||
|
animation: expand-animation .1s ease-in;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes expand-animation{
|
||||||
|
from{
|
||||||
|
height: 0;
|
||||||
|
transform: scaleY(0);
|
||||||
|
}
|
||||||
|
to{
|
||||||
|
transform: scaleY(1);
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hero {
|
.hero {
|
||||||
.navbar.darken {
|
.navbar.darken {
|
||||||
|
@ -17,13 +37,13 @@
|
||||||
// .navbar-item{
|
// .navbar-item{
|
||||||
// color: $primary;
|
// color: $primary;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
.has-inner-shadow-bottom{
|
.has-inner-shadow-bottom{
|
||||||
box-shadow: inset 0px -10px 15px -11px rgba(0,0,0,0.75);
|
box-shadow: inset 0px -10px 15px -11px rgba(0,0,0,0.75);
|
||||||
}
|
}
|
||||||
.has-inner-shadow-top{
|
.has-inner-shadow-top{
|
||||||
box-shadow: inset 0px 10px 15px -11px rgba(0,0,0,0.75);
|
box-shadow: inset 0px 10px 15px -11px rgba(0,0,0,0.75);
|
||||||
}
|
}
|
||||||
.has-shadow-top{
|
.has-shadow-top{
|
||||||
box-shadow: 0px 10px 15px -11px rgba(0,0,0,0.75);
|
box-shadow: 0px 10px 15px -11px rgba(0,0,0,0.75);
|
||||||
}
|
}
|
||||||
|
@ -40,19 +60,19 @@ $sizes: (
|
||||||
('lg', 2),
|
('lg', 2),
|
||||||
('xl', 4),
|
('xl', 4),
|
||||||
('xxl', 8),
|
('xxl', 8),
|
||||||
);
|
);
|
||||||
$positions: (
|
$positions: (
|
||||||
('t', 'top'),
|
('t', 'top'),
|
||||||
('r', 'right'),
|
('r', 'right'),
|
||||||
('b', 'bottom'),
|
('b', 'bottom'),
|
||||||
('l', 'left')
|
('l', 'left')
|
||||||
);
|
);
|
||||||
|
|
||||||
@function sizeValue($key, $value) {
|
@function sizeValue($key, $value) {
|
||||||
@return if($key == 'none', 0, $value + $sizeUnit);
|
@return if($key == 'none', 0, $value + $sizeUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@each $size in $sizes {
|
@each $size in $sizes {
|
||||||
$sizeKey: nth($size, 1);
|
$sizeKey: nth($size, 1);
|
||||||
$sizeValue: nth($size, 2);
|
$sizeValue: nth($size, 2);
|
||||||
.#{$marginKey}#{$separator}#{$sizeKey} {
|
.#{$marginKey}#{$separator}#{$sizeKey} {
|
||||||
|
@ -71,10 +91,10 @@ $positions: (
|
||||||
padding-#{$posValue}: sizeValue($sizeKey, $sizeValue);
|
padding-#{$posValue}: sizeValue($sizeKey, $sizeValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.m-t-a{
|
.m-t-a{
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
.m-b-a{
|
.m-b-a{
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
}
|
}
|
||||||
|
@ -84,48 +104,24 @@ $positions: (
|
||||||
.m-r-a{
|
.m-r-a{
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
// .app {
|
|
||||||
// .columns{
|
|
||||||
// margin-bottom: 0px !important;
|
|
||||||
// max-height: calc(100vh - 12.25rem);
|
|
||||||
// height: calc(100vh - 12.25rem);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
.app-content{
|
|
||||||
// height: 100vh;
|
|
||||||
}
|
|
||||||
.sidebar{
|
.sidebar{
|
||||||
// margin: auto;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 5em;
|
max-width: 5em;
|
||||||
width: 5em;
|
width: 5em;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
// background-color:rgba(127, 88, 145, 0.7);
|
|
||||||
// color: $beige-lighter;
|
|
||||||
border-right: thin #ccc solid;
|
border-right: thin #ccc solid;
|
||||||
|
|
||||||
}
|
}
|
||||||
.menu-titles{
|
.menu-titles{
|
||||||
margin-top: -2em;
|
margin-top: -2em;
|
||||||
}
|
}
|
||||||
.is-sidebar {
|
|
||||||
|
|
||||||
// transform: translate(15%, 0);
|
|
||||||
// background-color: rgba(0, 0, 0, .3);
|
|
||||||
}
|
|
||||||
.sidebar-menu{
|
.sidebar-menu{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 25px;
|
bottom: 25px;
|
||||||
left: 1em;
|
left: 1em;
|
||||||
}
|
}
|
||||||
.sideway-text{
|
|
||||||
// transform: translate(0, -50%);
|
|
||||||
// display: inline;
|
|
||||||
// margin: 0 0;
|
|
||||||
}
|
|
||||||
.sideway-letter{
|
.sideway-letter{
|
||||||
// width: 50%;
|
|
||||||
// height: 2em;
|
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
margin: -.8em auto;
|
margin: -.8em auto;
|
||||||
}
|
}
|
||||||
|
@ -134,14 +130,20 @@ $positions: (
|
||||||
min-height: calc(100vh - ( #{$navbar-height} ) );
|
min-height: calc(100vh - ( #{$navbar-height} ) );
|
||||||
max-height: calc(100vh - ( #{$navbar-height} ) );
|
max-height: calc(100vh - ( #{$navbar-height} ) );
|
||||||
height: calc(100vh - ( #{$navbar-height} ) );
|
height: calc(100vh - ( #{$navbar-height} ) );
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: stretch;
|
|
||||||
.column{
|
.column{
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.is-fullwidth{
|
||||||
|
width: 100vw;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.app-content{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
.avatar-badge-image{
|
.avatar-badge-image{
|
||||||
display: table;
|
display: table;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -223,18 +225,42 @@ $positions: (
|
||||||
// TODO: Remove this - make generic
|
// TODO: Remove this - make generic
|
||||||
.flipbook {
|
.flipbook {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60vh;
|
height: 75vh;
|
||||||
|
overflow-y: visible;
|
||||||
|
}
|
||||||
|
.book-flip-buttons{
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
.video-strip{
|
.video-strip{
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-content:center;
|
align-content:center;
|
||||||
|
max-height: 15vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
video{
|
video{
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
background-color:rgba(56, 181, 187, 0.19);
|
background-color:rgba(56, 181, 187, 0.19);
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
border: solid 1px rgba(56, 181, 187, 0.30);
|
border: solid 1px rgba(56, 181, 187, 0.30);
|
||||||
|
flex-basis: 100%;
|
||||||
|
max-width: 15vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.book-thumb{
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all .2s;
|
||||||
|
z-index: inherit;
|
||||||
|
flex-basis: 12%;
|
||||||
|
text-align: center;
|
||||||
|
&:hover{
|
||||||
|
transform: scale(1.1);
|
||||||
|
z-index: 10;
|
||||||
|
// box-shadow: 10px 0px 15px -13px rgba(0,0,0,0.35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.book-thumb:disabled:hover{
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<!-- <Header :appName="appName" /> -->
|
<!-- <Header :appName="appName" /> -->
|
||||||
|
<TopNavbar />
|
||||||
<div class="loading" v-if="loading">
|
<div class="loading" v-if="loading">
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,15 +14,10 @@
|
||||||
@onClose="onNotificationClose(notification)"
|
@onClose="onNotificationClose(notification)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns m-t-xs is-fullheight">
|
<div class="app-content m-t-xs is-fullheight">
|
||||||
<div class="column sidebar">
|
<div class="application">
|
||||||
<SideBar :title="appName" :menu="menu" :appName="appName" />
|
|
||||||
</div>
|
|
||||||
<section class="section column app-content">
|
|
||||||
<div class="container">
|
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,42 +29,20 @@ import Services from "../services/index";
|
||||||
import Notification from "../shared/components/Notification.vue";
|
import Notification from "../shared/components/Notification.vue";
|
||||||
import { mapActions, mapGetters } from "vuex";
|
import { mapActions, mapGetters } from "vuex";
|
||||||
import Loading from "../shared/components/Loading/Loading.vue";
|
import Loading from "../shared/components/Loading/Loading.vue";
|
||||||
|
import TopNavbar from "../shared/components/TopNavbar.vue";
|
||||||
import WebsocketService from "./scripts/websocket.service";
|
import WebsocketService from "./scripts/websocket.service";
|
||||||
// import AppRouter from "./router/router.vue";
|
// import AppRouter from "./router/router.vue";
|
||||||
import {
|
|
||||||
default as SideBar,
|
|
||||||
IMenuItem
|
|
||||||
} from "../shared/components/SideBar/SideBar.vue";
|
|
||||||
const menu: IMenuItem[] = [
|
|
||||||
{
|
|
||||||
href: "/",
|
|
||||||
text: "Home",
|
|
||||||
isRouterLink: true,
|
|
||||||
icon: "fa fa-home"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
href: "/settings",
|
|
||||||
isRouterLink: true,
|
|
||||||
text: "Settings",
|
|
||||||
icon: "fa fa-gears"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isRouterLink: false,
|
|
||||||
href: "/logout",
|
|
||||||
text: "Logout",
|
|
||||||
icon: "fa fa-sign-out"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
// Services.ApiService.getConnections();
|
// Services.ApiService.getConnections();
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
// router: AppRouter,
|
// router: AppRouter,
|
||||||
components: {
|
components: {
|
||||||
SideBar,
|
|
||||||
Header,
|
Header,
|
||||||
Notification,
|
Notification,
|
||||||
Loading
|
Loading,
|
||||||
|
TopNavbar
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
await this.getUser();
|
await this.getUser();
|
||||||
|
@ -90,7 +64,6 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
appName: "Seepur",
|
appName: "Seepur",
|
||||||
menu,
|
|
||||||
loading: true,
|
loading: true,
|
||||||
ws: null
|
ws: null
|
||||||
};
|
};
|
||||||
|
@ -104,7 +77,7 @@ export default {
|
||||||
message: `New call from ${payload.child.name}`,
|
message: `New call from ${payload.child.name}`,
|
||||||
level: "success"
|
level: "success"
|
||||||
});
|
});
|
||||||
this.$router.push({ path: `/call/${payload.callId}` });
|
this.$router.replace({ path: `/call/${payload.callId}` });
|
||||||
},
|
},
|
||||||
onNotificationClose(notification) {
|
onNotificationClose(notification) {
|
||||||
this.dismissNotification(notification.id);
|
this.dismissNotification(notification.id);
|
||||||
|
|
|
@ -6,12 +6,13 @@ export default class CallManager {
|
||||||
private localStream: MediaStream;
|
private localStream: MediaStream;
|
||||||
private remoteStream: MediaStream;
|
private remoteStream: MediaStream;
|
||||||
private signalingChannel;
|
private signalingChannel;
|
||||||
private needToAddStream: boolean = true;
|
|
||||||
private emitter = new EventEmitter();
|
private emitter = new EventEmitter();
|
||||||
private pc: RTCPeerConnection;
|
private pc: RTCPeerConnection;
|
||||||
public child;
|
public child;
|
||||||
public peer = { avatar: '' };
|
public peer = { avatar: '' };
|
||||||
public isHost = false;
|
public isHost = false;
|
||||||
|
public books = [];
|
||||||
|
public currentActivity = null;
|
||||||
constructor(private ws: WebSocketService, private callId: number, private userId: number) {
|
constructor(private ws: WebSocketService, private callId: number, private userId: number) {
|
||||||
this.inCall = false;
|
this.inCall = false;
|
||||||
this.peerId = null;
|
this.peerId = null;
|
||||||
|
@ -60,10 +61,11 @@ export default class CallManager {
|
||||||
...payload
|
...payload
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
async onCallStart(payload: { iceServers: RTCIceServer[], peerId: number, users: any[], child: any, hostId: number }) {
|
async onCallStart(payload: { iceServers: RTCIceServer[], peerId: number, users: any[], child: any, hostId: number, books: any[] }) {
|
||||||
console.log('onCallStart');
|
console.log('onCallStart');
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
this.peerId = payload.peerId;
|
this.peerId = payload.peerId;
|
||||||
|
this.books = payload.books;
|
||||||
this.isHost = this.peerId === payload.hostId;
|
this.isHost = this.peerId === payload.hostId;
|
||||||
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
|
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
|
||||||
this.child = payload.child;
|
this.child = payload.child;
|
||||||
|
@ -84,10 +86,11 @@ export default class CallManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onCallStandby(payload: { iceServers: RTCIceServer[], peerId: number, users: any[], child: any, hostId: number }) {
|
async onCallStandby(payload: { iceServers: RTCIceServer[], peerId: number, users: any[], child: any, hostId: number, books: any[] }) {
|
||||||
console.log('onCallStandby');
|
console.log('onCallStandby');
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
this.peerId = payload.peerId;
|
this.peerId = payload.peerId;
|
||||||
|
this.books = payload.books;
|
||||||
this.isHost = this.peerId === payload.hostId;
|
this.isHost = this.peerId === payload.hostId;
|
||||||
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
|
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
|
||||||
this.child = payload.child;
|
this.child = payload.child;
|
||||||
|
@ -102,6 +105,10 @@ export default class CallManager {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public selectBook(index: number) {
|
||||||
|
this.currentActivity = this.books[index];
|
||||||
|
}
|
||||||
|
|
||||||
setupPeerConnectionListeners() {
|
setupPeerConnectionListeners() {
|
||||||
this.pc.addEventListener("icecandidate", this.onLocalIce.bind(this));
|
this.pc.addEventListener("icecandidate", this.onLocalIce.bind(this));
|
||||||
this.pc.addEventListener('connectionstatechange', event => {
|
this.pc.addEventListener('connectionstatechange', event => {
|
||||||
|
@ -174,10 +181,7 @@ export default class CallManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
changeHost() {
|
changeHost() {
|
||||||
this.isHost = !this.isHost;
|
this.send('call:host:changed', {});
|
||||||
const hostId = this.isHost ? this.userId : this.peerId;
|
|
||||||
this.emit(ECallEvents.CALL_HOST_CHANGED, { hostId });
|
|
||||||
this.send('call:host:changed', { hostId });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemoteHostChanged(payload) {
|
onRemoteHostChanged(payload) {
|
||||||
|
|
|
@ -1207,7 +1207,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
|
||||||
}),_vm._v(" "),_c('div',{ref:"viewport",staticClass:"viewport",class:{
|
}),_vm._v(" "),_c('div',{ref:"viewport",staticClass:"viewport",class:{
|
||||||
zoom: _vm.zooming || _vm.zoom > 1,
|
zoom: _vm.zooming || _vm.zoom > 1,
|
||||||
'drag-to-scroll': _vm.dragToScroll
|
'drag-to-scroll': _vm.dragToScroll
|
||||||
},style:({ cursor: _vm.cursor == 'grabbing' ? 'grabbing' : 'auto' }),on:{"touchmove":_vm.onTouchMove,"pointermove":_vm.onPointerMove,"mousemove":_vm.onMouseMove,"touchend":_vm.onTouchEnd,"touchcancel":_vm.onTouchEnd,"pointerup":_vm.onPointerUp,"pointercancel":_vm.onPointerUp,"mouseup":_vm.onMouseUp,"wheel":_vm.onWheel}},[_c('div',{staticClass:"container",style:({ transform: ("scale(" + _vm.zoom + ")"), })},[_c('div',{style:({ transform: ("translateX(" + _vm.centerOffsetSmoothed + "px)") })},[(_vm.showLeftPage)?_c('img',{staticClass:"page fixed",style:({
|
},style:({ cursor: _vm.cursor == 'grabbing' ? 'grabbing' : 'auto' }),on:{"touchmove":_vm.onTouchMove,"pointermove":_vm.onPointerMove,"mousemove":_vm.onMouseMove,"touchend":_vm.onTouchEnd,"touchcancel":_vm.onTouchEnd,"pointerup":_vm.onPointerUp,"pointercancel":_vm.onPointerUp,"mouseup":_vm.onMouseUp,"wheel":_vm.onWheel}},[_c('div',{staticClass:"book-container",style:({ transform: ("scale(" + _vm.zoom + ")"), })},[_c('div',{style:({ transform: ("translateX(" + _vm.centerOffsetSmoothed + "px)") })},[(_vm.showLeftPage)?_c('img',{staticClass:"page fixed",style:({
|
||||||
width: _vm.pageWidth + 'px',
|
width: _vm.pageWidth + 'px',
|
||||||
height: _vm.pageHeight + 'px',
|
height: _vm.pageHeight + 'px',
|
||||||
left: _vm.xMargin + 'px',
|
left: _vm.xMargin + 'px',
|
||||||
|
@ -1244,11 +1244,11 @@ var __vue_staticRenderFns__ = [];
|
||||||
/* style */
|
/* style */
|
||||||
var __vue_inject_styles__ = function (inject) {
|
var __vue_inject_styles__ = function (inject) {
|
||||||
if (!inject) { return }
|
if (!inject) { return }
|
||||||
inject("data-v-0519f6d1_0", { source: ".viewport[data-v-0519f6d1]{-webkit-overflow-scrolling:touch;width:100%;height:100%}.viewport.zoom[data-v-0519f6d1]{overflow:scroll}.viewport.zoom.drag-to-scroll[data-v-0519f6d1]{overflow:hidden}.container[data-v-0519f6d1]{position:relative;width:100%;height:100%;transform-origin:top left;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip[data-v-0519f6d1]{position:absolute;width:50%;height:100%;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip.left[data-v-0519f6d1]{left:0}.click-to-flip.right[data-v-0519f6d1]{right:0}.bounding-box[data-v-0519f6d1]{position:absolute;-webkit-user-select:none;-ms-user-select:none;user-select:none}.page[data-v-0519f6d1]{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden}.polygon[data-v-0519f6d1]{position:absolute;top:0;left:0;background-repeat:no-repeat;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform-origin:center left}.polygon.blank[data-v-0519f6d1]{background-color:#ddd}.polygon .lighting[data-v-0519f6d1]{width:100%;height:100%}", map: undefined, media: undefined });
|
inject("data-v-16acf8ed_0", { source: ".viewport[data-v-16acf8ed]{-webkit-overflow-scrolling:touch;width:100%;height:95%}.viewport.zoom[data-v-16acf8ed]{overflow:scroll}.viewport.zoom.drag-to-scroll[data-v-16acf8ed]{overflow:hidden}.book-container[data-v-16acf8ed]{position:relative;width:100%;height:95%;transform-origin:top left;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip[data-v-16acf8ed]{position:absolute;width:50%;height:100%;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip.left[data-v-16acf8ed]{left:0}.click-to-flip.right[data-v-16acf8ed]{right:0}.bounding-box[data-v-16acf8ed]{position:absolute;-webkit-user-select:none;-ms-user-select:none;user-select:none}.page[data-v-16acf8ed]{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden}.polygon[data-v-16acf8ed]{position:absolute;top:0;left:0;background-repeat:no-repeat;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform-origin:center left}.polygon.blank[data-v-16acf8ed]{background-color:#ddd}.polygon .lighting[data-v-16acf8ed]{width:100%;height:100%}", map: undefined, media: undefined });
|
||||||
|
|
||||||
};
|
};
|
||||||
/* scoped */
|
/* scoped */
|
||||||
var __vue_scope_id__ = "data-v-0519f6d1";
|
var __vue_scope_id__ = "data-v-16acf8ed";
|
||||||
/* module identifier */
|
/* module identifier */
|
||||||
var __vue_module_identifier__ = undefined;
|
var __vue_module_identifier__ = undefined;
|
||||||
/* functional template */
|
/* functional template */
|
||||||
|
|
|
@ -1205,7 +1205,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
|
||||||
}),_vm._v(" "),_c('div',{ref:"viewport",staticClass:"viewport",class:{
|
}),_vm._v(" "),_c('div',{ref:"viewport",staticClass:"viewport",class:{
|
||||||
zoom: _vm.zooming || _vm.zoom > 1,
|
zoom: _vm.zooming || _vm.zoom > 1,
|
||||||
'drag-to-scroll': _vm.dragToScroll
|
'drag-to-scroll': _vm.dragToScroll
|
||||||
},style:({ cursor: _vm.cursor == 'grabbing' ? 'grabbing' : 'auto' }),on:{"touchmove":_vm.onTouchMove,"pointermove":_vm.onPointerMove,"mousemove":_vm.onMouseMove,"touchend":_vm.onTouchEnd,"touchcancel":_vm.onTouchEnd,"pointerup":_vm.onPointerUp,"pointercancel":_vm.onPointerUp,"mouseup":_vm.onMouseUp,"wheel":_vm.onWheel}},[_c('div',{staticClass:"container",style:({ transform: ("scale(" + _vm.zoom + ")"), })},[_c('div',{style:({ transform: ("translateX(" + _vm.centerOffsetSmoothed + "px)") })},[(_vm.showLeftPage)?_c('img',{staticClass:"page fixed",style:({
|
},style:({ cursor: _vm.cursor == 'grabbing' ? 'grabbing' : 'auto' }),on:{"touchmove":_vm.onTouchMove,"pointermove":_vm.onPointerMove,"mousemove":_vm.onMouseMove,"touchend":_vm.onTouchEnd,"touchcancel":_vm.onTouchEnd,"pointerup":_vm.onPointerUp,"pointercancel":_vm.onPointerUp,"mouseup":_vm.onMouseUp,"wheel":_vm.onWheel}},[_c('div',{staticClass:"book-container",style:({ transform: ("scale(" + _vm.zoom + ")"), })},[_c('div',{style:({ transform: ("translateX(" + _vm.centerOffsetSmoothed + "px)") })},[(_vm.showLeftPage)?_c('img',{staticClass:"page fixed",style:({
|
||||||
width: _vm.pageWidth + 'px',
|
width: _vm.pageWidth + 'px',
|
||||||
height: _vm.pageHeight + 'px',
|
height: _vm.pageHeight + 'px',
|
||||||
left: _vm.xMargin + 'px',
|
left: _vm.xMargin + 'px',
|
||||||
|
@ -1242,11 +1242,11 @@ var __vue_staticRenderFns__ = [];
|
||||||
/* style */
|
/* style */
|
||||||
var __vue_inject_styles__ = function (inject) {
|
var __vue_inject_styles__ = function (inject) {
|
||||||
if (!inject) { return }
|
if (!inject) { return }
|
||||||
inject("data-v-0519f6d1_0", { source: ".viewport[data-v-0519f6d1]{-webkit-overflow-scrolling:touch;width:100%;height:100%}.viewport.zoom[data-v-0519f6d1]{overflow:scroll}.viewport.zoom.drag-to-scroll[data-v-0519f6d1]{overflow:hidden}.container[data-v-0519f6d1]{position:relative;width:100%;height:100%;transform-origin:top left;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip[data-v-0519f6d1]{position:absolute;width:50%;height:100%;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip.left[data-v-0519f6d1]{left:0}.click-to-flip.right[data-v-0519f6d1]{right:0}.bounding-box[data-v-0519f6d1]{position:absolute;-webkit-user-select:none;-ms-user-select:none;user-select:none}.page[data-v-0519f6d1]{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden}.polygon[data-v-0519f6d1]{position:absolute;top:0;left:0;background-repeat:no-repeat;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform-origin:center left}.polygon.blank[data-v-0519f6d1]{background-color:#ddd}.polygon .lighting[data-v-0519f6d1]{width:100%;height:100%}", map: undefined, media: undefined });
|
inject("data-v-16acf8ed_0", { source: ".viewport[data-v-16acf8ed]{-webkit-overflow-scrolling:touch;width:100%;height:95%}.viewport.zoom[data-v-16acf8ed]{overflow:scroll}.viewport.zoom.drag-to-scroll[data-v-16acf8ed]{overflow:hidden}.book-container[data-v-16acf8ed]{position:relative;width:100%;height:95%;transform-origin:top left;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip[data-v-16acf8ed]{position:absolute;width:50%;height:100%;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip.left[data-v-16acf8ed]{left:0}.click-to-flip.right[data-v-16acf8ed]{right:0}.bounding-box[data-v-16acf8ed]{position:absolute;-webkit-user-select:none;-ms-user-select:none;user-select:none}.page[data-v-16acf8ed]{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden}.polygon[data-v-16acf8ed]{position:absolute;top:0;left:0;background-repeat:no-repeat;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform-origin:center left}.polygon.blank[data-v-16acf8ed]{background-color:#ddd}.polygon .lighting[data-v-16acf8ed]{width:100%;height:100%}", map: undefined, media: undefined });
|
||||||
|
|
||||||
};
|
};
|
||||||
/* scoped */
|
/* scoped */
|
||||||
var __vue_scope_id__ = "data-v-0519f6d1";
|
var __vue_scope_id__ = "data-v-16acf8ed";
|
||||||
/* module identifier */
|
/* module identifier */
|
||||||
var __vue_module_identifier__ = undefined;
|
var __vue_module_identifier__ = undefined;
|
||||||
/* functional template */
|
/* functional template */
|
||||||
|
|
|
@ -1388,7 +1388,7 @@
|
||||||
}),_vm._v(" "),_c('div',{ref:"viewport",staticClass:"viewport",class:{
|
}),_vm._v(" "),_c('div',{ref:"viewport",staticClass:"viewport",class:{
|
||||||
zoom: _vm.zooming || _vm.zoom > 1,
|
zoom: _vm.zooming || _vm.zoom > 1,
|
||||||
'drag-to-scroll': _vm.dragToScroll
|
'drag-to-scroll': _vm.dragToScroll
|
||||||
},style:({ cursor: _vm.cursor == 'grabbing' ? 'grabbing' : 'auto' }),on:{"touchmove":_vm.onTouchMove,"pointermove":_vm.onPointerMove,"mousemove":_vm.onMouseMove,"touchend":_vm.onTouchEnd,"touchcancel":_vm.onTouchEnd,"pointerup":_vm.onPointerUp,"pointercancel":_vm.onPointerUp,"mouseup":_vm.onMouseUp,"wheel":_vm.onWheel}},[_c('div',{staticClass:"container",style:({ transform: ("scale(" + _vm.zoom + ")"), })},[_c('div',{style:({ transform: ("translateX(" + _vm.centerOffsetSmoothed + "px)") })},[(_vm.showLeftPage)?_c('img',{staticClass:"page fixed",style:({
|
},style:({ cursor: _vm.cursor == 'grabbing' ? 'grabbing' : 'auto' }),on:{"touchmove":_vm.onTouchMove,"pointermove":_vm.onPointerMove,"mousemove":_vm.onMouseMove,"touchend":_vm.onTouchEnd,"touchcancel":_vm.onTouchEnd,"pointerup":_vm.onPointerUp,"pointercancel":_vm.onPointerUp,"mouseup":_vm.onMouseUp,"wheel":_vm.onWheel}},[_c('div',{staticClass:"book-container",style:({ transform: ("scale(" + _vm.zoom + ")"), })},[_c('div',{style:({ transform: ("translateX(" + _vm.centerOffsetSmoothed + "px)") })},[(_vm.showLeftPage)?_c('img',{staticClass:"page fixed",style:({
|
||||||
width: _vm.pageWidth + 'px',
|
width: _vm.pageWidth + 'px',
|
||||||
height: _vm.pageHeight + 'px',
|
height: _vm.pageHeight + 'px',
|
||||||
left: _vm.xMargin + 'px',
|
left: _vm.xMargin + 'px',
|
||||||
|
@ -1425,11 +1425,11 @@
|
||||||
/* style */
|
/* style */
|
||||||
var __vue_inject_styles__ = function (inject) {
|
var __vue_inject_styles__ = function (inject) {
|
||||||
if (!inject) { return }
|
if (!inject) { return }
|
||||||
inject("data-v-0519f6d1_0", { source: ".viewport[data-v-0519f6d1]{-webkit-overflow-scrolling:touch;width:100%;height:100%}.viewport.zoom[data-v-0519f6d1]{overflow:scroll}.viewport.zoom.drag-to-scroll[data-v-0519f6d1]{overflow:hidden}.container[data-v-0519f6d1]{position:relative;width:100%;height:100%;transform-origin:top left;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip[data-v-0519f6d1]{position:absolute;width:50%;height:100%;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip.left[data-v-0519f6d1]{left:0}.click-to-flip.right[data-v-0519f6d1]{right:0}.bounding-box[data-v-0519f6d1]{position:absolute;-webkit-user-select:none;-ms-user-select:none;user-select:none}.page[data-v-0519f6d1]{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden}.polygon[data-v-0519f6d1]{position:absolute;top:0;left:0;background-repeat:no-repeat;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform-origin:center left}.polygon.blank[data-v-0519f6d1]{background-color:#ddd}.polygon .lighting[data-v-0519f6d1]{width:100%;height:100%}", map: undefined, media: undefined });
|
inject("data-v-16acf8ed_0", { source: ".viewport[data-v-16acf8ed]{-webkit-overflow-scrolling:touch;width:100%;height:95%}.viewport.zoom[data-v-16acf8ed]{overflow:scroll}.viewport.zoom.drag-to-scroll[data-v-16acf8ed]{overflow:hidden}.book-container[data-v-16acf8ed]{position:relative;width:100%;height:95%;transform-origin:top left;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip[data-v-16acf8ed]{position:absolute;width:50%;height:100%;top:0;-webkit-user-select:none;-ms-user-select:none;user-select:none}.click-to-flip.left[data-v-16acf8ed]{left:0}.click-to-flip.right[data-v-16acf8ed]{right:0}.bounding-box[data-v-16acf8ed]{position:absolute;-webkit-user-select:none;-ms-user-select:none;user-select:none}.page[data-v-16acf8ed]{position:absolute;-webkit-backface-visibility:hidden;backface-visibility:hidden}.polygon[data-v-16acf8ed]{position:absolute;top:0;left:0;background-repeat:no-repeat;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform-origin:center left}.polygon.blank[data-v-16acf8ed]{background-color:#ddd}.polygon .lighting[data-v-16acf8ed]{width:100%;height:100%}", map: undefined, media: undefined });
|
||||||
|
|
||||||
};
|
};
|
||||||
/* scoped */
|
/* scoped */
|
||||||
var __vue_scope_id__ = "data-v-0519f6d1";
|
var __vue_scope_id__ = "data-v-16acf8ed";
|
||||||
/* module identifier */
|
/* module identifier */
|
||||||
var __vue_module_identifier__ = undefined;
|
var __vue_module_identifier__ = undefined;
|
||||||
/* functional template */
|
/* functional template */
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,6 +15,10 @@ import Settings from "../views/settings.vue";
|
||||||
import Call from "../views/call.vue";
|
import Call from "../views/call.vue";
|
||||||
import ChildProfile from "../views/child_profile.vue";
|
import ChildProfile from "../views/child_profile.vue";
|
||||||
|
|
||||||
|
// Call Views
|
||||||
|
import CallLobby from "../views/call_views/Lobby.vue";
|
||||||
|
import CallBook from "../views/call_views/Book.vue";
|
||||||
|
|
||||||
const routes: RouteConfig[] = [
|
const routes: RouteConfig[] = [
|
||||||
/** Define Application Routes */
|
/** Define Application Routes */
|
||||||
{
|
{
|
||||||
|
@ -28,7 +32,11 @@ const routes: RouteConfig[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/call/:id",
|
path: "/call/:id",
|
||||||
component: Call
|
component: Call,
|
||||||
|
children: [
|
||||||
|
{ path: "", component: CallLobby, name: "lobby" },
|
||||||
|
{ path: "book", component: CallBook, name: "book" }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/child/:id",
|
path: "/child/:id",
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuex, { Store } from "vuex";
|
import Vuex, { Store } from "vuex";
|
||||||
import Services from '../services';
|
import Services from '../services';
|
||||||
|
import CallManager, { ECallEvents } from "./classes/call.manager";
|
||||||
|
import WebsocketService from './scripts/websocket.service';
|
||||||
|
import { createContext } from 'vm';
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
const store = new Store({
|
const store = new Store({
|
||||||
strict: true,
|
strict: true,
|
||||||
state: {
|
state: {
|
||||||
user: null,
|
inCall: false,
|
||||||
|
callManager: null as CallManager,
|
||||||
|
user: { name: 'loading...', is_admin: false, id: null },
|
||||||
notifications: []
|
notifications: []
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
@ -14,6 +19,12 @@ const store = new Store({
|
||||||
},
|
},
|
||||||
notifications(state) {
|
notifications(state) {
|
||||||
return state.notifications;
|
return state.notifications;
|
||||||
|
},
|
||||||
|
callManager(state): CallManager {
|
||||||
|
return state.callManager;
|
||||||
|
},
|
||||||
|
inCall(state) {
|
||||||
|
return state.inCall;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
@ -30,10 +41,18 @@ const store = new Store({
|
||||||
},
|
},
|
||||||
dismissNotification(state, noteId: number) {
|
dismissNotification(state, noteId: number) {
|
||||||
state.notifications = state.notifications.filter(n => n.id != noteId);
|
state.notifications = state.notifications.filter(n => n.id != noteId);
|
||||||
|
},
|
||||||
|
callEnded(state) {
|
||||||
|
state.callManager = null;
|
||||||
|
state.inCall = false;
|
||||||
|
},
|
||||||
|
connectToCall(state, options: { callId: number, ws: any }) {
|
||||||
|
state.callManager = new CallManager(options.ws, options.callId, state.user.id);
|
||||||
|
state.inCall = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
getUser: async (ctx, userId?: number) => {
|
async getUser(ctx, userId?: number) {
|
||||||
const user = await Services.ApiService.getUser(userId);
|
const user = await Services.ApiService.getUser(userId);
|
||||||
ctx.commit('setUser', user);
|
ctx.commit('setUser', user);
|
||||||
},
|
},
|
||||||
|
@ -42,6 +61,17 @@ const store = new Store({
|
||||||
},
|
},
|
||||||
dismissNotification(ctx, id: number) {
|
dismissNotification(ctx, id: number) {
|
||||||
ctx.commit("dismissNotification", id);
|
ctx.commit("dismissNotification", id);
|
||||||
|
},
|
||||||
|
callEnded(ctx) {
|
||||||
|
ctx.commit('callEnded');
|
||||||
|
},
|
||||||
|
async connectToCall(ctx, callId: string) {
|
||||||
|
if (!ctx.state.inCall) {
|
||||||
|
const ws = await WebsocketService.getInstance();
|
||||||
|
ctx.commit('connectToCall', { callId, ws });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,89 +1,42 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="is-fullwidth">
|
||||||
<div v-if="loading">
|
<div v-if="loading">
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class>
|
<div v-else class>
|
||||||
<div class="floating-host is-flex" style="position:fixed;top:60px;left:100px">
|
<VideoStrip :localStream="localStream" :remoteStream="remoteStream" />
|
||||||
<h1 class="subtitle m-r-md">Host:</h1>
|
<router-view></router-view>
|
||||||
<div class="me">
|
|
||||||
<figure class="image is-24x24">
|
|
||||||
<img
|
|
||||||
:src="isHost? user.avatar : callManager.peer.avatar"
|
|
||||||
class="is-rounded is-avatar"
|
|
||||||
@click="changeHost()"
|
|
||||||
/>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="video-strip m-b-md is-outlined">
|
|
||||||
<video
|
|
||||||
:src-object.prop.camel="localStream"
|
|
||||||
autoplay
|
|
||||||
playsinline
|
|
||||||
muted="true"
|
|
||||||
style="max-width:20%"
|
|
||||||
/>
|
|
||||||
<video :src-object.prop.camel="remoteStream" autoplay style="max-width:20%" />
|
|
||||||
</div>
|
|
||||||
<div class="is-flex">
|
|
||||||
<div class="go-left m-r-sm" style="display: flex; align-items: center;">
|
|
||||||
<button class="button is-outlined" :disabled="!isHost" @click="onLeftClicked()">
|
|
||||||
<i class="fa fa-fw fa-arrow-left"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<flipbook
|
|
||||||
class="flipbook"
|
|
||||||
:pages="createPages(book)"
|
|
||||||
forwardDirection="left"
|
|
||||||
:zooms="null"
|
|
||||||
:enabled="isHost"
|
|
||||||
@flip-left-end="onFlip('left')"
|
|
||||||
@flip-right-end="onFlip('right')"
|
|
||||||
ref="flipbook"
|
|
||||||
v-slot="flipbook"
|
|
||||||
>
|
|
||||||
<div class="page-progress has-text-centered m-b-none">
|
|
||||||
<p>Page {{ flipbook.page }} of {{ flipbook.numPages }}</p>
|
|
||||||
</div>
|
|
||||||
</flipbook>
|
|
||||||
<div class="go-right m-l-sm" style="display: flex; align-items: center;">
|
|
||||||
<button class="button is-outlined" :disabled="!isHost" @click="onRightClicked()">
|
|
||||||
<i class="fa fa-fw fa-arrow-right"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Loading from "../../shared/components/Loading/Loading.vue";
|
import Loading from "../../shared/components/Loading/Loading.vue";
|
||||||
import WebsocketService from "../scripts/websocket.service";
|
import { ECallEvents } from "../classes/call.manager";
|
||||||
import CallManager, { ECallEvents } from "../classes/call.manager";
|
import VideoStrip from "./call_views/VideoStrip.vue";
|
||||||
import Flipbook from "../components/flipbook/flipbook.cjs.js";
|
|
||||||
import Services from "../../services/index";
|
import Services from "../../services/index";
|
||||||
import { mapActions, mapGetters } from "vuex";
|
import { mapActions, mapGetters } from "vuex";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Loading,
|
Loading,
|
||||||
Flipbook
|
VideoStrip
|
||||||
},
|
},
|
||||||
name: "Call",
|
name: "Call",
|
||||||
mounted() {
|
// mounted() {
|
||||||
const self = this;
|
// const self = this;
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
self.isMounted = true;
|
// self.isMounted = true;
|
||||||
}, 1000);
|
// }, 1000);
|
||||||
},
|
// },
|
||||||
async created() {
|
async created() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const callId = Number(this.$route.params.id);
|
const callId = Number(this.$route.params.id);
|
||||||
const ws = await WebsocketService.getInstance();
|
|
||||||
this.callManager = new CallManager(ws, callId, this.user.id);
|
await this.connectToCall(callId);
|
||||||
|
|
||||||
this.callManager.on(ECallEvents.CLOSE, this.callEnded);
|
this.callManager.on(ECallEvents.CLOSE, this.callEnded);
|
||||||
this.callManager.on(ECallEvents.ACTION_BOOK_FLIP, this.onRemoteFlip);
|
|
||||||
const success = await this.callManager.connectToCall({
|
const success = await this.callManager.connectToCall({
|
||||||
video: true,
|
video: true,
|
||||||
audio: true
|
audio: true
|
||||||
|
@ -110,71 +63,38 @@ export default {
|
||||||
async beforeDestroy() {
|
async beforeDestroy() {
|
||||||
console.log("destroyed");
|
console.log("destroyed");
|
||||||
this.callManager.close();
|
this.callManager.close();
|
||||||
|
this.$store.dispatch("callEnded");
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async setupCall(): Promise<boolean> {
|
async setupCall(): Promise<boolean> {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onFlip(direction: "left" | "right") {
|
|
||||||
if (this.isHost)
|
|
||||||
this.callManager.send(`book:action:flip-page`, { direction });
|
|
||||||
},
|
|
||||||
onLeftClicked() {
|
|
||||||
this.$refs.flipbook.flipLeft();
|
|
||||||
},
|
|
||||||
onRightClicked() {
|
|
||||||
this.$refs.flipbook.flipRight();
|
|
||||||
},
|
|
||||||
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) {
|
callEnded(callId) {
|
||||||
this.notify({ message: `Call #${callId} Ended` });
|
this.notify({ message: `Call #${callId} Ended` });
|
||||||
this.$router.push({ path: `/` });
|
this.$router.replace({ path: `/` });
|
||||||
},
|
},
|
||||||
onRemoteHostChanged(payload) {
|
onRemoteHostChanged(payload) {
|
||||||
console.log("-----------");
|
console.log("-----------");
|
||||||
console.log(payload);
|
console.log(payload);
|
||||||
this.peer = this.callManager.peer;
|
this.peer = this.callManager.peer;
|
||||||
this.isHost = this.callManager.isHost;
|
|
||||||
},
|
},
|
||||||
changeHost() {
|
changeHost() {
|
||||||
this.callManager.changeHost();
|
this.callManager.changeHost();
|
||||||
},
|
},
|
||||||
...mapActions(["notify"])
|
...mapActions(["notify", "connectToCall"])
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["user"])
|
...mapGetters(["user", "callManager", "inCall"])
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
book: {
|
|
||||||
title: "Taylor",
|
|
||||||
pages: 34
|
|
||||||
},
|
|
||||||
loading: true,
|
loading: true,
|
||||||
localStream: null,
|
localStream: null,
|
||||||
remoteStream: null,
|
remoteStream: null
|
||||||
callManager: null,
|
|
||||||
isHost: false,
|
|
||||||
// currentPage: 0,
|
// currentPage: 0,
|
||||||
// totalPages: 34,
|
// totalPages: 34,
|
||||||
isMounted: false
|
// isMounted: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
beforeCreate: () => {}
|
beforeCreate: () => {}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<div class="book-view">
|
||||||
|
<!-- <nav class="level">
|
||||||
|
<div class="level-left">
|
||||||
|
<div class="level-item">
|
||||||
|
<button class="button" @click="$router.back()">Back</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>-->
|
||||||
|
<div class="is-flex">
|
||||||
|
<div class="go-left m-r-sm" style="display: flex; align-items: center;">
|
||||||
|
<button
|
||||||
|
class="button book-flip-buttons"
|
||||||
|
:disabled="!callManager.isHost"
|
||||||
|
@click="onLeftClicked()"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-arrow-left"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<flipbook
|
||||||
|
class="flipbook"
|
||||||
|
:pages="createPages(callManager.currentActivity)"
|
||||||
|
:forwardDirection="callManager.currentActivity.ltr ? 'right': 'left'"
|
||||||
|
:zooms="null"
|
||||||
|
:enabled="callManager.isHost"
|
||||||
|
@flip-left-end="onFlip('left')"
|
||||||
|
@flip-right-end="onFlip('right')"
|
||||||
|
ref="flipbook"
|
||||||
|
v-slot="flipbook"
|
||||||
|
>
|
||||||
|
<div class="page-progress has-text-centered m-b-none">
|
||||||
|
<p>Page {{ flipbook.page }} of {{ flipbook.numPages }}</p>
|
||||||
|
</div>
|
||||||
|
</flipbook>
|
||||||
|
<div class="go-right m-l-sm" style="display: flex; align-items: center;">
|
||||||
|
<button
|
||||||
|
class="button book-flip-buttons"
|
||||||
|
:disabled="!callManager.isHost"
|
||||||
|
@click="onRightClicked()"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-arrow-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Flipbook from "../../components/flipbook/flipbook.cjs.js";
|
||||||
|
import { ECallEvents } from "../../classes/call.manager";
|
||||||
|
import { mapActions, mapGetters } from "vuex";
|
||||||
|
export default {
|
||||||
|
name: "CallBook",
|
||||||
|
components: { Flipbook },
|
||||||
|
created() {
|
||||||
|
this.callManager.on(ECallEvents.ACTION_BOOK_FLIP, this.onRemoteFlip);
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
localStream: null,
|
||||||
|
remoteStream: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(["user", "callManager"])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onFlip(direction: "left" | "right") {
|
||||||
|
if (this.isHost)
|
||||||
|
this.callManager.send(`book:action:flip-page`, { direction });
|
||||||
|
},
|
||||||
|
onLeftClicked() {
|
||||||
|
this.$refs.flipbook.flipLeft();
|
||||||
|
},
|
||||||
|
onRightClicked() {
|
||||||
|
this.$refs.flipbook.flipRight();
|
||||||
|
},
|
||||||
|
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/books/${book.id}/page/${i}`);
|
||||||
|
}
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="Books">
|
||||||
|
<h2 class="subtitle">
|
||||||
|
<i class="fa fa-fw fa-book"></i> Select A Book
|
||||||
|
</h2>
|
||||||
|
<div class="is-flex m-b-md">
|
||||||
|
<div
|
||||||
|
class="book-thumb m-l-md"
|
||||||
|
v-for="(book, index) in callManager.books"
|
||||||
|
:key="index"
|
||||||
|
@click="goToBook(book, index)"
|
||||||
|
:disabled="!callManager.isHost"
|
||||||
|
>
|
||||||
|
<div class="book-cover">
|
||||||
|
<figure class="image is-2by3 m-a">
|
||||||
|
<img :src="`/u/books/${book.id}/page/1`" />
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<div class="book-text">
|
||||||
|
<div>{{book.title}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { mapGetters } from "vuex";
|
||||||
|
export default {
|
||||||
|
name: "CallLobby",
|
||||||
|
methods: {
|
||||||
|
goToBook(book, index) {
|
||||||
|
this.callManager.selectBook(index);
|
||||||
|
this.$router.replace({ name: "book" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(["callManager"])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="video-strip m-b-md">
|
||||||
|
<video :src-object.prop.camel="localStream" autoplay playsinline muted="true" />
|
||||||
|
<video :src-object.prop.camel="remoteStream" autoplay />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "CallVideoStrip",
|
||||||
|
props: ["remoteStream", "localStream"]
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="container">
|
||||||
<div class="loading" v-if="loading">
|
<div class="loading" v-if="loading">
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="parents">
|
<div class="parents">
|
||||||
<div class="columns">
|
<div class="is-flex">
|
||||||
<AvatarBadge
|
<AvatarBadge
|
||||||
class="column"
|
class="column"
|
||||||
v-for="parent in child.parents"
|
v-for="parent in child.parents"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="container">
|
||||||
<div class="loading" v-if="loading">
|
<div class="loading" v-if="loading">
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
<i class="fa fa-fw fa-book"></i> My Books
|
<i class="fa fa-fw fa-book"></i> My Books
|
||||||
</h2>
|
</h2>
|
||||||
<div class="is-flex m-b-md">
|
<div class="is-flex m-b-md">
|
||||||
<div class="book m-l-md" v-for="i of [1, 2, 3, 4]" :key="i">
|
<div class="book-thumb m-l-md" v-for="i of [1, 2, 3, 4]" :key="i">
|
||||||
<div class="book-cover">
|
<div class="book-cover">
|
||||||
<figure class="image is-2by3 m-a">
|
<figure class="image is-2by3 m-a">
|
||||||
<img
|
<img
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="container">
|
||||||
<div class="loading" v-if="loading">
|
<div class="loading" v-if="loading">
|
||||||
<Loading />
|
<Loading />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,7 +10,12 @@
|
||||||
</div>
|
</div>
|
||||||
<aside class="menu is-primary sidebar-menu">
|
<aside class="menu is-primary sidebar-menu">
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
<li v-for="item in menu" class="m-t-md m-b-md" @click="onItemClicked(item)">
|
<li
|
||||||
|
v-for="item in menu"
|
||||||
|
class="m-t-md m-b-md"
|
||||||
|
@click="onItemClicked(item)"
|
||||||
|
:key="item.text"
|
||||||
|
>
|
||||||
<router-link
|
<router-link
|
||||||
active-class="is-active"
|
active-class="is-active"
|
||||||
v-if="item.isRouterLink"
|
v-if="item.isRouterLink"
|
||||||
|
|
115
resources/scripts/applications/shared/components/TopNavbar.vue
Normal file
115
resources/scripts/applications/shared/components/TopNavbar.vue
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<template>
|
||||||
|
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<router-link to="/" class="navbar-item" exact v-if="!inCall">
|
||||||
|
<strong>Seepur</strong>
|
||||||
|
</router-link>
|
||||||
|
<!-- <a class="navbar-item" href="/">
|
||||||
|
</a>-->
|
||||||
|
<a
|
||||||
|
id="menu-button"
|
||||||
|
role="button"
|
||||||
|
class="navbar-burger burger"
|
||||||
|
@click="showMenu=!showMenu"
|
||||||
|
aria-label="menu"
|
||||||
|
aria-expanded="false"
|
||||||
|
data-target="navbarBasicExample"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="nav-menu" :class="['navbar-menu', {'is-active':showMenu}]">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<!-- Normal Nav -->
|
||||||
|
<router-link active-class="is-active" to="/" class="navbar-item" exact v-if="!inCall">Home</router-link>
|
||||||
|
<router-link
|
||||||
|
active-class="is-active"
|
||||||
|
to="/about"
|
||||||
|
class="navbar-item"
|
||||||
|
exact
|
||||||
|
v-if="!inCall"
|
||||||
|
>About</router-link>
|
||||||
|
<!-- In call Nav -->
|
||||||
|
<div class="navbar-item" v-if="inCall">
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<p class="control">
|
||||||
|
<router-link
|
||||||
|
active-class="is-active"
|
||||||
|
to="/"
|
||||||
|
class="button is-danger"
|
||||||
|
exact
|
||||||
|
replace
|
||||||
|
v-if="inCall"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-times-circle-o"></i> End Call
|
||||||
|
</router-link>
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<router-link
|
||||||
|
:to="{path:'..'}"
|
||||||
|
class="button is-info"
|
||||||
|
append
|
||||||
|
replace
|
||||||
|
v-if="inCall && $route.name==='book'"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-arrow-circle-o-left"></i> Back
|
||||||
|
</router-link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item has-dropdown is-hoverable is-dark" v-if="!inCall">
|
||||||
|
<a class="navbar-link">{{user.name}}</a>
|
||||||
|
|
||||||
|
<div class="navbar-dropdown">
|
||||||
|
<router-link active-class="is-active" to="/settings" class="navbar-item" exact>Settings</router-link>
|
||||||
|
<a class="navbar-item" href="/logout">Logout</a>
|
||||||
|
<hr class="navbar-divider" v-if="user.is_admin" />
|
||||||
|
<a class="navbar-item" href="/admin/" v-if="user.is_admin">Admin Settigns</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-item" v-if="inCall">
|
||||||
|
<p class="control">
|
||||||
|
<button class="button" @click="changeHost()">
|
||||||
|
<i class="fa fa-fw fa-refresh"></i> Change Host
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="inCall" class="navbar-item">Current Host: {{host.name}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { mapGetters, mapActions } from "vuex";
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showMenu: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
host() {
|
||||||
|
if (this.inCall) {
|
||||||
|
if (this.callManager.isHost) return this.user;
|
||||||
|
return this.callManager.peer;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
...mapGetters(["user", "inCall", "callManager"])
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeHost() {
|
||||||
|
this.callManager.changeHost();
|
||||||
|
},
|
||||||
|
...mapActions([])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@!component('components.nav.nav', isLanding = false, auth=auth)
|
{{-- @!component('components.nav.nav', isLanding = false, auth=auth) --}}
|
||||||
|
|
||||||
@!section('hero')
|
@!section('hero')
|
||||||
<div class="">
|
<div class="">
|
||||||
|
|
|
@ -42,6 +42,7 @@ const globalMiddleware =
|
||||||
auth: 'Adonis/Middleware/Auth',
|
auth: 'Adonis/Middleware/Auth',
|
||||||
guest: 'Adonis/Middleware/AllowGuestOnly',
|
guest: 'Adonis/Middleware/AllowGuestOnly',
|
||||||
adminAuth: 'App/Middleware/AdminAuth',
|
adminAuth: 'App/Middleware/AdminAuth',
|
||||||
|
BookPageAuth: 'App/Middleware/BookPageAuth'
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -44,6 +44,12 @@ Route
|
||||||
.prefix('api/v1/client')
|
.prefix('api/v1/client')
|
||||||
.middleware(['auth']);
|
.middleware(['auth']);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private book assets
|
||||||
|
*/
|
||||||
|
Route.get('/u/books/:bookId/page/:pageNumber', 'BookApiController.getPage')
|
||||||
|
.middleware(['auth', 'BookPageAuth']);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
/ Pubic CDN Images
|
/ Pubic CDN Images
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue