muaz-khan / RTCMultiConnection

RTCMultiConnection is a WebRTC JavaScript library for peer-to-peer applications (screen sharing, audio/video conferencing, file sharing, media streaming etc.)
https://muazkhan.com:9001/
MIT License
2.57k stars 1.37k forks source link

Same layout on other users. #628

Open ph4t0o0o0om opened 6 years ago

ph4t0o0o0om commented 6 years ago

https://uploads.disquscdn.com/images/fc4a5dd3a520efc32c5ebc1c32cc42467f3fb7b3cdd894b3bf6e038fe1e3327b.png

Hi Muaz Khan thank you for very helpful library. Please check the link for the picture .

Now this is the screenshot of my screen. If we can see there a two videos on 1 page. Sir Is it possible that the layout of the video of one user is the same to others? Like on the picture this our 2 different browser joining on the same room and if we can see the layout of the video is different . If who is the LOCAL video of the room that is on the first (as always) . Now I repeat sir is it possible that the layout of the video is the same layout seeing by other users?

https://uploads.disquscdn.com/images/4d37f938fac54950ebbc3cf5c4580782747280b775f1acb5f1a0beebb9b08395.png

Example on the second image. This is the layout seeing by other users. The big layout on the top is the teacher and the small video on the bottom is teachers. And it displays the same layout in all users.

Please help me . BIG THANKS SIR!!!

muaz-khan commented 6 years ago

HTML:

<section class="videos-layout">
    <div class="teacher-big-screen"></div>
    <div class="students-videos"></div>
</section>

CSS: (Set Styles Yourself)

.videos-layout {}
.teacher-big-screen {}
.students-videos {}

.teacher-big-screen video {
    width: 100%;
}

.students-videos video {
    width: 360px;
    margin: 0 5px;
}

JavaScript:

connection.onstream = function(event) {
    if(event.stream.isScreen) {
        $('.teacher-big-screen').html('').append(event.mediaElement);
        return;
    }

    $('.students-videos').append(event.mediaElement);
};

Code for the Presenter ONLY

connection.session = {
    audio: true,
    screen: true,
    // broadcast: true : Enable this line ONLY if you want ONE_to_Many broadcast
};
connection.dontCaptureUserMedia = true;

// manually capture screen and microphone
getScreenConstraints(function(error, screen_constraints) {
    navigator.mediaDevices.getUserMedia({
        video: screen_constraints
    }).then(function(screen) {
        var video = document.querySelector('video');
        video.muted = true;
        video.srcObject = screen;

        navigator.mediaDevices.getUserMedia({
            audio: true
        }).then(function(mic) {
            var finalStream = new MediaStream();
            screen.getVideoTracks().concat(mic.getAudioTracks()).forEach(function(track) {
                finalStream.addTrack(track);
            });
            finalStream.isScreen = true;
            connection.attachStreams.push(finalStream);

            // video.srcObject = finalStream;

            var event = {
                type: 'local',
                stream: finalStream,
                mediaElement: video
            };
            connection.onstream(event);
        });
    });
});

Code for all participants

connection.session = {
    audio: true,
    video: true,
    // broadcast: true : Enable this line ONLY if you want ONE_to_Many broadcast
};
ph4t0o0o0om commented 6 years ago

Do I'm going to place all of these codes on index.html?

ph4t0o0o0om commented 6 years ago

@muaz-khan The code is about screen sharing i don't need screen sharing sir. What I need sir is same videos layout in all users. I think you miss interpret my concern.

ph4t0o0o0om commented 6 years ago

PLEASE HELP ME .

This is my code for index.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0, user-scalable=no">
    <title></title>
</head>
<body>

 <link rel="stylesheet" type="text/css" href="/home/mainlms.css">
<script src="canvas-designer-widget.js"></script>

<div id="widget-container" style="position: fixed;bottom: 0;top:-0%;right: 20%;left: 23%;height: 100%;border: 2px solid black; border-top:0; border-bottom: 0; width: 75%;"></div>

<div id="fade" class="black_overlay"></div>
<!-- VIDEO CONTROLS -->
<!--<div id="action-controls" style="width: 19%; padding: 1%;position: absolute;left:0;">-->
<div id="action-controls" style="width: 18%; padding: 1%;position: absolute;left:0;">

  <!--  <div id="videos-container"></div>-->
<!--ADDITIONAL CODE FOR MULTI CONFERENCE-->
<div id="local-videos-container"></div>

<hr>

<div id="remote-videos-container"></div>

    <div>
        <!--Number of users connected with you:--> <b style="display: none;" id="number-of-connected-users">0</b>

        <br><br>

        <div id="hide-on-datachannel-opened">

            <br><br>

            <input type="text" id="room-id" placeholder="room-id">
            <button id="open-room">Open</button>

  <div class="border" id="canvasDiv"></div>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
  <script type="text/javascript" src="js/pdf.js"></script>
  <script src="js/socket.io.js"></script>
  <script src="js/app.bundle.js"></script>

        </div>
    </div>

</div>

<div class="extra-controls">
    <!--<button id="export-as-image">Export as Image</button>--->
    <button id="btn-display-undo-popup">Undo</button>
</div>

<div id="light" class="white_content">
    <button id="btn-close-undo-popup" style="float:right;">x</button>

    <select id="undo-options" size="4">
        <option>Last Shape</option>
        <option>All Shapes</option>
        <option>Last Multiple</option>
        <option disabled>Specific Range</option>
    </select>

    <div style="display: none;margin-top: 20px;margin-bottom: 20px;margin-right: 12px;float: right;">
        Digit/Number:<br>
        <input type="text" id="number-of-shapes-to-undo" style="padding: 0;width: 90px;text-align: center;border-radius: 0;margin-top: 6px;">
    </div>

    <br><br><br><br><br>

    <button id="btn-undo" style="float:right;">Undo</button>
</div>

<div id="dataURL-popup" class="white_content">
    <button id="btn-close-dataURL-popup" style="float:right;">x</button>

    <select id="data-url-format" size="4">
        <option>image/png</option>
        <option>image/jpeg</option>
        <option>image/gif</option>
        <option>image/webp</option>
    </select>

        <a id="link-to-image" target="_blank" download="image.png"></a>

    <br><br><br><br><br>

    <button id="btn-getDataURL" style="float:right;">Get DataURL</button>
</div>

<div id="comments-popup" class="white_content">
    <button id="btn-close-comments-popup" style="float:right;">x</button>

    </section>

</div>

<div style="position: fixed; right: 0; width: 19%; padding: 1%;top:0; text-align: center;">

</body>
<script>
document.getElementById('room-id').onkeyup = document.getElementById('room-id').onblur = function() {
    localStorage.setItem('canvas-designer-roomid', this.value);
};

if (localStorage.getItem('canvas-designer-roomid')) {
    document.getElementById('room-id').value = localStorage.getItem('canvas-designer-roomid');
}

document.getElementById('open-room').onclick = function() {
    var roomid = document.getElementById('room-id').value;
    if (!roomid.length) return alert('Please enter roomid.');

    this.disabled = true;

    connection.open(roomid, onOpenRoom);

    this.parentNode.innerHTML = '<a href="#' + roomid + '" target="_blank">Please share this link</a>';
};

var designer = new CanvasDesigner();

// you can place widget.html anywhere
designer.widgetHTML = 'widget.html';
designer.widgetJsURL = 'widget.min.js'

designer.addSyncListener(function(data) {
    connection.send(data);
});

designer.setSelected('pencil');

designer.setTools({
    pencil: true,
    text: true,
    image: true,
    eraser: true,
    line: true,
    arrow: true,
    dragSingle: true,
    dragMultiple: true,
    arc: true,
    rectangle: true,
    quadratic: false,
    bezier: false,
    marker: true,
    zoom: true
});

designer.appendTo(document.getElementById('widget-container'));

Array.prototype.slice.call(document.getElementById('action-controls').querySelectorAll('input[type=checkbox]')).forEach(function(checkbox) {
    checkbox.onchange = function() {
        designer.destroy();

        designer.addSyncListener(function(data) {
            connection.send(data);
        });

        var tools = {};
        Array.prototype.slice.call(document.getElementById('action-controls').querySelectorAll('input[type=checkbox]')).forEach(function(checkbox2) {
            if (checkbox2.checked) {
                tools[checkbox2.id] = true;
            }
        });
        designer.setTools(tools);
        designer.appendTo(document.getElementById('widget-container'));
    };
});

var undoOptions = document.getElementById('undo-options');

document.getElementById('btn-display-undo-popup').onclick = function() {
    document.getElementById('light').style.display = 'block';
    document.getElementById('fade').style.display = 'block';
};

var txtNumberOfShapesToUndo = document.getElementById('number-of-shapes-to-undo');
txtNumberOfShapesToUndo.onkeyup = function() {
    localStorage.setItem('number-of-shapes-to-undo', txtNumberOfShapesToUndo.value);
}

if (localStorage.getItem('number-of-shapes-to-undo')) {
    txtNumberOfShapesToUndo.value = localStorage.getItem('number-of-shapes-to-undo');
    txtNumberOfShapesToUndo.onkeyup();
}

undoOptions.onchange = function() {
    txtNumberOfShapesToUndo.parentNode.style.display = 'none';

    if (undoOptions.value === 'Specific Range') {
        //
    } else if (undoOptions.value === 'Last Multiple') {
        txtNumberOfShapesToUndo.parentNode.style.display = 'block';
    }

    localStorage.setItem('undo-options', undoOptions.value);
};

undoOptions.onclick = undoOptions.onchange;

if (localStorage.getItem('undo-options')) {
    undoOptions.value = localStorage.getItem('undo-options');
    undoOptions.onchange();
}

document.getElementById('btn-undo').onclick = function() {
    if (undoOptions.value === 'All Shapes') {
        designer.undo('all');
    } else if (undoOptions.value === 'Specific Range') {
        designer.undo({
            specificRange: {
                start: -1,
                end: -1
            }
        });
    } else if (undoOptions.value === 'Last Shape') {
        designer.undo(-1);
    } else if (undoOptions.value === 'Last Multiple') {
        var numberOfLastShapes = txtNumberOfShapesToUndo.value;
        numberOfLastShapes = parseInt(numberOfLastShapes || 0) || 0;
        designer.undo({
            numberOfLastShapes: numberOfLastShapes
        });
    }

    closeUndoPopup();
};

function closeUndoPopup() {
    document.getElementById('light').style.display = 'none';
    document.getElementById('fade').style.display = 'none';

    undoOptions.onchange();
}
document.getElementById('btn-close-undo-popup').onclick = closeUndoPopup;

function closeDataURLPopup() {
    document.getElementById('dataURL-popup').style.display = 'none';
    document.getElementById('fade').style.display = 'none';

    dataURLFormat.onchange();
}
document.getElementById('btn-close-dataURL-popup').onclick = closeDataURLPopup;

document.getElementById('export-as-image').onclick = function() {
    linkToImage.innerHTML = linkToImage.href = linkToImage.style = '';

    document.getElementById('dataURL-popup').style.display = 'block';
    document.getElementById('fade').style.display = 'block';

    getDataURL();
};

function getDataURL(callback) {
    callback = callback || function() {};
    var format = dataURLFormat.value;
    designer.toDataURL(format || 'image/png', function(dataURL) {
        linkToImage.style = 'margin-left: 10px;display: block;text-align: center;margin-bottom: -50px;';
        linkToImage.href = dataURL;
        linkToImage.innerHTML = 'Click to Download Image';
        linkToImage.download = 'image.' + (format || 'image/png').split('/')[1];

        callback(dataURL, format);
    });
}

var dataURLFormat = document.getElementById('data-url-format');
var linkToImage = document.getElementById('link-to-image');

dataURLFormat.onchange = function() {
    localStorage.setItem('data-url-format', dataURLFormat.value);
    getDataURL();
};
dataURLFormat.onclick = dataURLFormat.onchange;

if (localStorage.getItem('data-url-format')) {
    dataURLFormat.value = localStorage.getItem('data-url-format');
    dataURLFormat.onchange();
}

document.getElementById('btn-getDataURL').onclick = function() {
    getDataURL(function(dataURL, format) {
        window.open(dataURL);
    });

    // closeDataURLPopup();
};

document.getElementById('btn-close-comments-popup').onclick = function() {
    document.getElementById('comments-popup').style.display = 'none';
    document.getElementById('fade').style.display = 'none';

    dataURLFormat.onchange();
};

function showCommentsPopup(e) {
    document.getElementById('comments-popup').style.display = 'block';
    document.getElementById('fade').style.display = 'block';
}
document.getElementById('btn-comments').onclick = showCommentsPopup;
if (location.hash.length && location.hash.indexOf('comment') !== -1) {
    showCommentsPopup();
}
</script>

<script src="js/RTCMultiConnection.min.js"></script>
<script src="js/socket.io.js"></script>

<script>
//--------------------------------- BACKUP FREE SIGNALING SERVER ----------------------------------------\\
//connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';    \\
//--------------------------------------------------------------------------------------------------------\\

var connection = new RTCMultiConnection();
//--------------------------------------------------------------------------------------------------------\\
//OUR OWN LOCAL SERVER\\
connection.socketURL = 'http://mydomaim:port/';
//connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
//--------------------------------------------------------------------------------------------------------\\

connection.socketMessageEvent = 'canvas-designer';

connection.enableFileSharing =false;
connection.session = {
    audio: true,
    video: true,
    data: true
};
connection.sdpConstraints.mandatory = {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
};

/*var localVideosContainer = document.getElementById('local-videos-container');
var remoteVideosContainer = document.getElementById('remote-videos-container');

connection.onstream = function (event){

    var video = event.mediaElement;

   if(event.type ==='local'){
    localVideosContainer.appendChild (video);
   }

    if(event.type ==='remote'){
    remoteVideosContainer.appendChild( video ) ; 
   }

}*/
//THIS FUNCTION IS FOR FIXING THE ECHO (HOPEFULLY IT FIXED)
function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) {
    startedTimes = (startedTimes || 0) + 1;
    if (startedTimes >= numberOfTimes) return;

    setTimeout(function() {
        callback();
        afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes);
    }, setTimeoutInteval);
}

connection.onunmute = function(event) {
    // event.isAudio == audio-only-stream
    // event.audio == has audio tracks

    if (event.isAudio || event.session.audio) {
        // set volume=0
        event.mediaElement.volume = 0;

        // steadily increase volume
        afterEach(200, 5, function() {
            event.mediaElement.volume += .20;
        });
    }
};
//for video feature
connection.dontCaptureUserMedia = false;
if (location.hash.replace('#', '').length) {
    var roomid = location.hash.replace('#', '');
    connection.join(roomid);
}

//THIS IS FOR LABEL IF THE USERS IS ONLINE OR OFFLINE

/*connection.onUserStatusChanged = function(event) {
    var infoBar = document.getElementById('hide-on-datachannel-opened');
    if (event.status == 'online') {
        infoBar.innerHTML = event.userid + ' is <b >online</b>.';
    }

    if (event.status == 'offline') {
        infoBar.innerHTML = event.userid + ' is <b>offline</b>.';
    }

    numberOfConnectedUsers.innerHTML = connection.getAllParticipants().length;
};*/

var numberOfConnectedUsers = document.getElementById('number-of-connected-users');
connection.onopen = function(event) {
    var infoBar = document.getElementById('hide-on-datachannel-opened');
    infoBar.innerHTML = '<b style="display:none;">' + event.userid + '<b style="display: none;"> is ready to collaborate with you.</b>';

    if (designer.pointsLength <= 0) {
        // make sure that remote user gets all drawings synced.
        setTimeout(function() {
            connection.send('plz-sync-points');
        }, 1000);
    }

    numberOfConnectedUsers.innerHTML = connection.getAllParticipants().length;
};

connection.onclose = connection.onerror = connection.onleave = function() {
    numberOfConnectedUsers.innerHTML = connection.getAllParticipants().length;
};

connection.onmessage = function(event) {
    if (event.data === 'plz-sync-points') {
        designer.sync();
        return;
    }

    designer.syncData(event.data);
}
//THIS code is for undo AGAD AGAD mag sysync sa other users
function afterUndoOrAfterRedo() {
    designer.sync();
};
</script>

<script src="dev/webrtc-handler.js"></script>
<script>
function onOpenRoom() {
    // capture canvas-2d stream
    // and share in realtime using RTCPeerConnection.addStream
    // requires: dev/webrtc-handler.js
    //for the preveiew feature of canvas
    video.captureStream(function(stream) {
        connection.attachStreams = [stream];
        connection.onstream({
            stream: stream
        });
    });
}

connection.dontCaptureUserMedia = false;
var localVideosContainer = document.getElementById('local-videos-container');
var remoteVideosContainer = document.getElementById('remote-videos-container');
connection.onstream = function(event) {
   if(event.type === 'local'){
         localVideosContainer.appendChild(event.mediaElement);
}
    if(event.type === 'remote'){
        remoteVideosContainer.appendChild(event.mediaElement);
}
};

/*connection.onstreamended = function() {
    video.src = null;
    video.style.display = 'none';
};*/
connection.onstreamended = function(event) {
    var video = document.getElementById(event.streamid);
    if (video && video.parentNode) {
        video.parentNode.removeChild(event.mediaElement);
    }
};
</script>

</body>
</html>

This is my code for the CSS 

<style>
* {
    -webkit-user-select: none;
    -moz-user-select: none;
    -o-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
input[type=text] {
    -webkit-user-select: initial;
    -moz-user-select: initial;
    -o-user-select: initial;
    -ms-user-select: initial;
    user-select: initial;
}

button, input {
    font-family: Myriad, Arial, Verdana;
    font-weight: normal;
    border-top-left-radius: 3px;
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
    border-bottom-left-radius: 3px;
    padding: 4px 12px;
    text-decoration: none;
    color: rgb(27, 26, 26);
    display: inline-block;
    box-shadow: rgb(255, 255, 255) 1px 1px 0px 0px inset;
    text-shadow: none;
    background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0.05, rgb(241, 241, 241)), to(rgb(230, 230, 230)));
    font-size: 20px;
    border: 1px solid red;
    outline:none;
}
video {
    width: 100%;
    border-radius: 10px;
     border-width:3px;  
    border-style:solid;
    border-color: grey;

}

button:hover, button:hover, input:focus {
    background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(5%, rgb(221, 221, 221)), to(rgb(250, 250, 250)));
    border: 1px solid rgb(142, 142, 142);
}
button[disabled], input[disabled] {
    background: rgb(249, 249, 249);
    border: 1px solid rgb(218, 207, 207);
    color: rgb(197, 189, 189);
}

input {
    background: white;
}

.extra-controls {
    position: absolute;
    right: 21%;
}

#room-id {
    width: 55%;
    margin-right: 2px;
}

/* popup box */
.black_overlay {
    display: none;
    position: absolute;
    top: 0%;
    left: 0%;
    width: 100%;
    height: 100%;
    background-color: black;
    z-index:1001;
    -moz-opacity: 0.8;
    opacity:.80;
    filter: alpha(opacity=80);
}
.white_content {
    display: none;
    position: absolute;
    top: 30%;
    left: 40%;
    width: 16%;
    height: 15%;
    padding: 16px;
    border: 10px solid rgb(9, 159, 243);
    background-color: rgba(255, 255, 255, 0.94);
    z-index:1002;
    overflow: hidden;
    border-radius: 5px;
    min-width: 300px;
    min-height: 120px;
}

.white_content hr {
    border-top: 4px solid rgb(9, 159, 243);
    margin: 9px -20px;
}

#btn-undo, #btn-close-undo-popup, #btn-close-dataURL-popup, #btn-close-comments-popup, #btn-getDataURL {
    font-size: 1.2em;
    padding: 2px 9px;
}

#btn-close-undo-popup, #btn-close-dataURL-popup, #btn-close-comments-popup {
    padding: 0 6px;
    border-radius: 50%;
    padding-bottom: 1px;
    color: red;
}

.white_content select {
    font-size: 1.2em;
    border: 1px solid;
    padding: 0;
    outline: none!important;
    border: 1px solid black !important;
    float: left;
}
.white_content select option {
    padding: 2px 5px;
    border-bottom: 1px solid black;
}
.white_content select option:last-child {
    border-bottom: 0;
}

.white_content option:checked, .white_content option[selected] {
    background-color: #06CDFF!important;
    background:linear-gradient(#06CDFF, #06CDFF)!important;
    color: white!important;
}

.white_content select[disabled] {
    background-color: rgba(232, 229, 229, 0.17);
    color: rgb(84, 82, 82);
}

div#comments-popup {
    width: 100%;
    left: 0;
    top: 0;
    height: 100%;
    border: 0;
    padding: 0;
    border-radius: 0;
    overflow: auto;
}

#btn-comments {
    color: red;
    margin-top: 5px;
    font-size: 24px;
    text-shadow: 1px 1px white;
}

#h2-close-popup {
    background: yellow;
    padding: 5px 10px;
    border-bottom: 1px solid red;
    margin: 0;
    color: red;
}

</style>

THIS IS THE OUTPUT WHEN I RUN . THIS IS THE VIEW OF USER 1. help1

THIS IS THE VIEW OF USER 2 help 2

THIS IS THE INSPECT ELEMENT.

help3

WHAT I NEED IS SAME LAYOUT OF VIDEOS VIEWED BY ALL USERS. AND THE USER 1 OR THE ROOM CREATOR IS THE BIGGER SCREEN COMPARE ON OTHER PARTICIPANTS .

AND THIS IS THE LAYOUT I WANT TO ACHIEVE .

layouttoacchieve

muaz-khan commented 6 years ago

Just added a demo: demos/dashboard.html

Online URL:

ph4t0o0o0om commented 6 years ago

Wow thank you so much muaz-khan.

1 thing the name in the chat is not friendly name it is a random scrambled letters. How can I add a enter a name first and that name will appear to chat .

Thank you so much!!!

On Sat, Sep 15, 2018 at 5:54 PM, Muaz Khan notifications@github.com wrote:

Just added a demo: demos/dashboard.html https://github.com/muaz-khan/RTCMultiConnection/blob/master/demos/dashboard.html

Online URL:

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/muaz-khan/RTCMultiConnection/issues/628#issuecomment-421547606, or mute the thread https://github.com/notifications/unsubscribe-auth/AnlPnHiaDgjLZ_bAdqCwqqizw0ez9A8yks5ubM5MgaJpZM4WmnS3 .

ph4t0o0o0om commented 6 years ago

I know that is a connection.extra API event i follow the codes on your youtube video entatiled add extra. But I think your code there is deprecated because it do nothing when I add the codes on my codes.

Please help thank you for being a kind person.

On Sat, Sep 15, 2018 at 11:46 PM, Benedict Aquino benchakino04@gmail.com wrote:

Wow thank you so much muaz-khan.

1 thing the name in the chat is not friendly name it is a random scrambled letters. How can I add a enter a name first and that name will appear to chat .

Thank you so much!!!

On Sat, Sep 15, 2018 at 5:54 PM, Muaz Khan notifications@github.com wrote:

Just added a demo: demos/dashboard.html https://github.com/muaz-khan/RTCMultiConnection/blob/master/demos/dashboard.html

Online URL:

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/muaz-khan/RTCMultiConnection/issues/628#issuecomment-421547606, or mute the thread https://github.com/notifications/unsubscribe-auth/AnlPnHiaDgjLZ_bAdqCwqqizw0ez9A8yks5ubM5MgaJpZM4WmnS3 .

muaz-khan commented 6 years ago

Now users can enter their full names.

Please check the demo again:

ph4t0o0o0om commented 6 years ago

Last thing sir please help me this is the major part I never did.

How to add a pdf to canvas and sync to all users? And if one user switch the pdf it will sync to all users .

How it that possible Mr . muaz-khan

On Sun, Sep 16, 2018 at 12:20 AM, Muaz Khan notifications@github.com wrote:

Now users can enter their full names.

Please check the demo again:

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/muaz-khan/RTCMultiConnection/issues/628#issuecomment-421594024, or mute the thread https://github.com/notifications/unsubscribe-auth/AnlPnDTDeqDB66s1NK0PNyILp28J0YqBks5ubSi7gaJpZM4WmnS3 .

muaz-khan commented 6 years ago

PDF syncing is possible. I'll add this feature as well (in the same demo).

ph4t0o0o0om commented 6 years ago

WOW !!! Thank you so very much mr muaz-khan!!!

I can't explain how happy I A'm because I'm doing that feature for almost 2 weeks and nothing happens even tho read many articles many blogs.

I'm going to wait for this now sir.

Again. THANK YOU SO MUCH!!

On Sun, Sep 16, 2018 at 12:38 AM, Muaz Khan notifications@github.com wrote:

PDF syncing is possible. I'll add this feature as well (in the same demo).

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/muaz-khan/RTCMultiConnection/issues/628#issuecomment-421596699, or mute the thread https://github.com/notifications/unsubscribe-auth/AnlPnN5ivse3-8rSIs5Ghxkig3nRycP1ks5ubS0VgaJpZM4WmnS3 .