Hello I am trying to integrate video chat between two users in angular 8 Using webRTC but I am unable to do that.Is there any suggestion how will i do that in angula8 please help me out.
component.html
<div class="videoCall">
<video #localVideo class="self" playsinline autoplay></video>
<video #remoteVideo class="self" style="margin-left: 10px;" playsinline autoplay></video>
</div>
<div>
<button #startButton [disabled]="startButtonDisabled" (click)="start()">Start</button>
<button #callButton [disabled]="callButtonDisabled" (click)="call()">Call</button>
<button #hangupButton [disabled]="hangupButtonDisabled" (click)="hangup()">Hang Up</button>
</div>
componenet.ts
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '#angular/core';
import * as signalR from "#aspnet/signalr";
import { VideoCallService } from '../../common/services/video-call.service';
// import{ConnectionManager} from '../right-connect/connectionManager.component';
import adapter from 'webrtc-adapter';
#Component({
selector: 'right-connect',
templateUrl: './right-connect.component.html',
styleUrls: ['./right-connect.component.scss']
})
export class RightConnectComponent implements AfterViewInit {
#ViewChild('startButton', {static: false}) startButton: ElementRef;
#ViewChild('callButton', {static: false}) callButton: ElementRef;
#ViewChild('hangupButton', {static: false}) hangupButton: ElementRef;
#ViewChild('localVideo', {static: false}) localVideo: ElementRef;
#ViewChild('remoteVideo', {static: false}) remoteVideo: ElementRef;
startButtonDisabled = false;
callButtonDisabled = true;
hangupButtonDisabled = true;
startTime;
localStream;
pc1;
pc2;
offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
ngAfterViewInit() {
this.localVideo.nativeElement.addEventListener('loadedmetadata', ()=> {
this.trace('Local video videoWidth: ' + this.localVideo.nativeElement.videoWidth +
'px, videoHeight: ' + this.localVideo.nativeElement.videoHeight + 'px');
});
this.remoteVideo.nativeElement.addEventListener('loadedmetadata', ()=> {
this.trace('Remote video videoWidth: ' + this.remoteVideo.nativeElement.videoWidth +
'px, videoHeight: ' + this.remoteVideo.nativeElement.videoHeight + 'px');
});
this.remoteVideo.nativeElement.onresize = ()=> {
this.trace('Remote video size changed to ' +
this.remoteVideo.nativeElement.videoWidth + 'x' + this.remoteVideo.nativeElement.videoHeight);
};
}
getName(pc) {
return (pc === this.pc1) ? 'pc1' : 'pc2';
}
getOtherPc(pc) {
return (pc === this.pc1) ? this.pc2 : this.pc1;
}
gotStream(stream) {
this.trace('Received local stream');
this.localVideo.nativeElement.srcObject = stream;
this.localStream = stream;
this.callButtonDisabled = false;
}
start() {
this.trace('Requesting local stream');
this.startButtonDisabled = true;
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(this.gotStream.bind(this))
.catch(function(e) {
console.log('error', e);
alert('getUserMedia() error: ' + e.name);
});
}
call() {
this.callButtonDisabled = true;
this.hangupButtonDisabled = false;
this.trace('Starting call');
this.startTime = window.performance.now();
var videoTracks = this.localStream.getVideoTracks();
var audioTracks = this.localStream.getAudioTracks();
if (videoTracks.length > 0) {
this.trace('Using video device: ' + videoTracks[0].label);
}
if (audioTracks.length > 0) {
this.trace('Using audio device: ' + audioTracks[0].label);
}
var servers = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]};
this.pc1 = new RTCPeerConnection(servers);
this.trace('Created local peer connection object pc1');
/*this.pc1.onicecandidate = e => {
this.onIceCandidate(this.pc1, e);
};*/
this.pc1.addEventListener('icecandidate', (e: Event) => {
this.onIceCandidate(this.pc1, e);
});
this.pc2 = new RTCPeerConnection(servers);
this.trace('Created remote peer connection object pc2');
/*this.pc2.onicecandidate = e => {
this.onIceCandidate(this.pc2, e);
};*/
this.pc2.addEventListener('icecandidate', (e: Event) => {
this.onIceCandidate(this.pc2, e);
});
/*this.pc1.oniceconnectionstatechange = e => {
this.onIceStateChange(this.pc1, e);
};
this.pc2.oniceconnectionstatechange = e => {
this.onIceStateChange(this.pc2, e);
};*/
this.pc1.addEventListener('iceconnectionstatechange', (e: Event) => {
this.onIceStateChange(this.pc1, e);
});
this.pc2.addEventListener('iceconnectionstatechange', (e: Event) => {
this.onIceStateChange(this.pc2, e);
});
this.pc2.ontrack = this.gotRemoteStream.bind(this);
this.localStream.getTracks().forEach(
track => {
this.trace('add tracks to pc1');
this.pc1.addTrack(
track,
this.localStream
);
}
);
this.trace('Added local stream to pc1');
this.trace('pc1 createOffer start');
this.pc1.createOffer(
this.offerOptions
).then(
this.onCreateOfferSuccess.bind(this),
this.onCreateSessionDescriptionError.bind(this)
);
}
onCreateSessionDescriptionError(error) {
this.trace('Failed to create session description: ' + error.toString());
}
onCreateOfferSuccess(desc) {
this.trace('Offer from pc1\n' + desc.sdp);
this.trace('pc1 setLocalDescription start');
this.pc1.setLocalDescription(desc).then(
() => {
this.onSetLocalSuccess(this.pc1);
},
this.onSetSessionDescriptionError.bind(this)
);
this.trace('pc2 setRemoteDescription start');
this.pc2.setRemoteDescription(desc).then(
() => {
this.onSetRemoteSuccess(this.pc2);
},
this.onSetSessionDescriptionError.bind(this)
);
this.trace('pc2 createAnswer start');
// Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
this.pc2.createAnswer().then(
this.onCreateAnswerSuccess.bind(this),
this.onCreateSessionDescriptionError.bind(this)
);
}
onSetLocalSuccess(pc) {
this.trace(this.getName(pc) + ' setLocalDescription complete');
}
onSetRemoteSuccess(pc) {
this.trace(this.getName(pc) + ' setRemoteDescription complete');
}
onSetSessionDescriptionError(error) {
this.trace('Failed to set session description: ' + error.toString());
}
gotRemoteStream(e) {
if (this.remoteVideo.nativeElement.srcObject !== e.streams[0]) {
this.remoteVideo.nativeElement.srcObject = e.streams[0];
this.trace('pc2 received remote stream');
}
}
onCreateAnswerSuccess(desc) {
this.trace('Answer from pc2:\n' + desc.sdp);
this.trace('pc2 setLocalDescription start');
this.pc2.setLocalDescription(desc).then(
() => {
this.onSetLocalSuccess(this.pc2);
},
this.onSetSessionDescriptionError.bind(this)
);
this.trace('pc1 setRemoteDescription start');
this.pc1.setRemoteDescription(desc).then(
() => {
this.onSetRemoteSuccess(this.pc1);
},
this.onSetSessionDescriptionError.bind(this)
);
}
onIceCandidate(pc, event) {
this.getOtherPc(pc).addIceCandidate(event.candidate)
.then(
() => {
this.onAddIceCandidateSuccess(pc);
},
(err) => {
this.onAddIceCandidateError(pc, err);
}
);
this.trace(this.getName(pc) + ' ICE candidate: \n' + (event.candidate ?
event.candidate.candidate : '(null)'));
}
onAddIceCandidateSuccess(pc) {
this.trace(this.getName(pc) + ' addIceCandidate success');
}
onAddIceCandidateError(pc, error) {
this.trace(this.getName(pc) + ' failed to add ICE Candidate: ' + error.toString());
}
onIceStateChange(pc, event) {
if (pc) {
this.trace(this.getName(pc) + ' ICE state: ' + pc.iceConnectionState);
console.log('ICE state change event: ', event);
}
}
hangup() {
this.trace('Ending call');
this.pc1.close();
this.pc2.close();
this.pc1 = null;
this.pc2 = null;
this.hangupButtonDisabled = true;
this.callButtonDisabled = false;
}
trace(arg) {
var now = (window.performance.now() / 1000).toFixed(3);
console.log(now + ': ', arg);
}
}
component.scss
.videoCall{
display: flex;
.self{
width: 264px;
height: 298px;
border: 2px solid black;
}
.partner{
margin-left: 10px;
width: 264px;
height: 298px;
border: 2px solid black;
}
}
I am unable to call between two users. how will I do that. Or is there any other way to integrate video call in angualr 8 please suggest me. Please help me out
Thanks
Related
I have the following scenario for a chatroon with Symfony 5.4 and MercureBundle. Chat conversation function like a charm, but I don't understand how to include additional info (username) into a payload config of mercure to get connected users (subscribed users to a chatroom topic) from the subscriptions list with the condition that I use mercure() twig extension.
Environment vars:
return array(
'MERCURE_URL' => 'https://pami54.local/.well-known/mercure',
'MERCURE_PUBLIC_URL' => 'https://pami54.local/.well-known/mercure',
'MERCURE_JWT_SECRET' => 'm3rcu353cr37pa55pra53DEV',
);
mercure.yaml:
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: ['notif/unreaded/{user}', 'notif/mailbox/unreaded/{buzon}', 'app/chatroom', '/.well-known/mercure/subscriptions/{topic}/{subscriber}']
subscribe: ['/.well-known/mercure/subscriptions/{topic}/{subscriber}']
The following controller renders in the base template the variables needed to generate the EventSource object (declared topics):
/**
*
* #param Request $request
* #param UuidEncoder $uuidEncoder
* #param UserInterface $user
* #param LoggerInterface $logger
* #return Response
*/
public function mercureTopicsAction(Request $request, UuidEncoder $uuidEncoder, UserInterface $user, LoggerInterface $logger): Response
{
try {
$incluirMessageChatTopic = $request->get('includeMessageChatTopic', false);
$userIdPublico = $uuidEncoder->encode($user->getIdPublico());
$buzonInternoId = $uuidEncoder->encode($user->getBuzonInterno()->getIdPublico());
/**
* Se establecen desde el controller los 3 topic de notificaciones existenes.
* * */
$notifUnreadedTopic = sprintf("notif/unreaded/%s", $userIdPublico);
$notifMailboxTopic = sprintf("notif/mailbox/unreaded/%s", $buzonInternoId);
$messagesChatroomTopic = "app/chatroom";
$subscriptionsTopic = sprintf("/.well-known/mercure/subscriptions%s/%s", "?topic=" . $messagesChatroomTopic, $user->getIdPublico());
} catch (\Exception $exc) {
$logger->error(sprintf("[%s:%s]: %s", self::class, __FUNCTION__, $exc->getMessage()));
return $this->render('App\_topics.html.twig', ['topics' => [], 'subscriptionsTopic' => "", 'username' => "nobody", 'hubServer' => ""]);
}
$topics = [];
$topics[] = $notifUnreadedTopic;
$topics[] = $notifMailboxTopic;
$topics[] = $messagesChatroomTopic;
$topics[] = $subscriptionsTopic;
return $this->render('App\_topics.html.twig', ['topics' => $topics, 'subscriptionsTopic' => $subscriptionsTopic, 'username' => $user->getUserIdentifier(), 'hubServer' => $this->getParameter('app.MERCURE_URL')]);
}
The view fragment (_topics.html.twig) generated by the controller above:
{% set config = {'mercureHub':mercure(topics, { subscribe:topics}), 'subscriptionsTopic':subscriptionsTopic, 'username':username, 'hubServer':hubServer} %}
<script type="application/json" id="mercure">
{{ config|json_encode(constant('JSON_UNESCAPED_SLASHES') b-or constant('JSON_HEX_TAG'))|raw }}
</script>
The javascript code:
const {mercureHub, subscriptionsTopic, username, hubServer} = $.parseJSON($('#mercure').text());
let mercureHubURL = mercureHub;
let eventSource;
(async () => {
const resp = await fetch(new URL(subscriptionsTopic, hubServer), {
credentials: "include",
});
const subscriptionCollection = await resp.json();
console.log(subscriptionCollection);
mercureHubURL = mercureHubURL + '&lastEventID=' + subscriptionCollection.lastEventID;
eventSource = new ReconnectingEventSource(mercureHubURL, {withCredentials: true, max_retry_time: 60000});
eventSource.onopen = function () {
console.log('Conexion establecida con el socket!');
};
// eventSource.onmessage = function () {
// console.log('Unknow message!!!!');
// };
eventSource.onerror = function () {
console.log('Error de conexion al socket!');
};
eventSource.addEventListener("notif", function (e) {
if (e.type === 'notif') {
let parsedData = null;
try {
parsedData = $.parseJSON(e.data);
} catch (error) {
console.log(error);
}
if (parsedData) {
$('#totalNotificacionesNoAtendidas').html(parsedData.total);
let list = $('#lista-notificaciones');
list.html('');
$.each(parsedData.notificaciones, function (i, e) {
list.append('<li><div class="row"><div class="col-md-12 text-sm text-left ' + e.iconoColor + '"> <i class="' + e.icono + '"></i> ' + e.asunto + '</div></div><div class="row"><div class="col-md-12 text-right text-sm"> <a class="link-muted lnkCursorPuntero lnkModalDetallesNotificacion" data-src="' + Routing.generate('panel_notificaciones_detalle', {'id': e.id}) + '"><i class="fa fa-eye"></i> detalles</a> </div></div></li><li role="separator" class="divider"></li>');
});
}
} else {
console.log('Incoming unknown message');
}
}, false);
eventSource.addEventListener("mailbox", function (e) {
if (e.type === 'mailbox') {
let parsedData = null;
try {
parsedData = $.parseJSON(e.data);
} catch (error) {
console.log(error);
}
if (parsedData) {
$('.totalMensajesNoLeidosBuzonInternoRecibidos').html(parsedData.total);
let list = $('#lista-notificaciones-buzon');
list.html('');
$.each(parsedData.mensajes, function (i, e) {
list.append('<li><div class="pull-left"><img class="img-circle" src="' + e.foto + '" alt="User image"/></div><h4>' + e.remitente + '</h4><p class="text-truncate">' + e.asunto + '</p></li>');
});
if (parsedData.mostrarMensajeRecibido === true) {
toastr.info("Tiene un nuevo mensaje privado", 'Mensaje recibido', {
timeOut: 8000,
preventDuplicates: false,
positionClass: 'toast-top-right',
progressBar: true,
showDuration: 800,
hideDuration: 400,
extendedTimeOut: 1000,
showEasing: "swing",
hideEasing: "linear",
showMethod: "fadeIn",
hideMethod: "fadeOut",
});
}
}
} else {
console.log('Incoming unknown message');
}
}, false);
eventSource.addEventListener("chatroom", function (e) {
if (e.type === 'chatroom') {
let msg = null;
try {
msg = $.parseJSON(e.data);
} catch (error) {
console.log(error);
}
if (msg !== null) {
let chatConversationElement = $('#chat-conversations');
let sender = chatConversationElement.data('sender');
let classOnline = sender === msg.sender ? 'online' : '';
chatConversationElement.append('<div class="item" id="' + msg.idMensaje + '" data-sender="' + msg.sender + '"><img src="' + msg.foto + '" alt="user image" class="' + classOnline + '"><p class="message"><a class="name lnkCursorPuntero"><small class="text-muted pull-right"><i class="fa fa-clock-o"></i> ' + msg.time + '</small>' + msg.sender + '</a>' + msg.texto + '</p></div>');
chatConversationElement.slimScroll({scrollBy: '400px'});
}
} else {
console.log('Incoming unknown message');
}
}, false);
eventSource.addEventListener("Subscription", function (e) {
if (e.type === 'Subscription') {
let msg = null;
try {
msg = JSON.parse(e.data);
} catch (error) {
console.log(error);
}
console.log("Nueva suscripcion !!!");
} else {
console.log('Incoming unknown message');
}
}, false);
})();
The chat messaging works ok, but the subscription event is not fired. The other important thing, how include a username into a payload array of a JWS if I use Cookie Authorization mechanism?
I don't understand that part of the Mercure configuration.
I successfully uploaded the image to firebase storage but I'm having trouble to store image url in firestore.
I received an error on console when trying to view the image:
core.js:6462 WARNING: sanitizing unsafe URL value
C:\fakepath\coke.jpg (see http://g.co/ng/security#xss)
GET unsafe:C:\fakepath\coke.jpg net::ERR_UNKNOWN_URL_SCHEME
Below is the html code: (details.page.html)
<!-- PRODUCT PICTURE INPUT -->
<ion-item>
<ion-label position="stacked">Product Picture</ion-label>
<ion-input accept="image/*" type="file" name="productPics" formControlName="productPics" (change)="showPreview($event)"></ion-input>
</ion-item>
Below is the typescript code: (details.page.ts)
ngOnInit() {
this.productForm = this.fb.group({
productPics: new FormControl('', Validators.compose([
Validators.required,
])),
});
this.resetForm();
}
showPreview(event: any) {
if (event.target.files && event.target.files[0]) {
const reader = new FileReader();
reader.onload = (e: any) => this.imgSrc = e.target.result;
reader.readAsDataURL(event.target.files[0]);
this.selectedImage = event.target.files[0];
} else {
this.imgSrc = "./assets/default_image.jpg";
this.selectedImage = null;
}
}
async saveProduct(formValue) {
this.isSubmitted = true;
this.product.userId = this.authService.getAuth().currentUser.uid;
if (this.productId) {
try {
this.product.createdAt = new Date().getTime();
console.log('product add');
console.log(this.productForm.value);
var filePath = `${formValue.productCategory}/${this.selectedImage.name}${new Date().getTime()}`;
const fileRef = this.storage.ref(filePath);
this.storage.upload(filePath, this.selectedImage).snapshotChanges().pipe(
finalize(() => {
fileRef.getDownloadURL().subscribe((url) => {
formValue['productPics'] = url;
this.service.insertImageDetails(formValue);
this.resetForm();
})
})
).subscribe();
await this.productService.addProduct(this.product);
await this.loading.dismiss();
this.navCtrl.navigateBack('/vendor-tabs/home-vendor');
} catch (error) {
console.log('product dont add');
this.presentToast('Error trying to save');
}
}
}
Below is the service: (product.service.ts)
private productsCollection: AngularFirestoreCollection<Product>;
addProduct(product: Product) {
return this.productsCollection.add(product);
}
getProduct(id: string) {
return this.productsCollection.doc<Product>(id).valueChanges();
}
updateProduct(id: string, product: Product) {
return this.productsCollection.doc<Product>(id).update(product);
}
deleteProduct(id: string) {
return this.productsCollection.doc(id).delete();
}
insertImageDetails(image) {
this.productsCollection.add(image);
}
Currently, I am implementing a chat. After user pressed a chat button, the app will navigate the user to the Chat component. The chat content will simply store in firebase and chatId is needed to identify which chat belongs to the user.
Since I don't know how to pass props during navigation, I decided to save the CurrentChatId in AsyncStorage. After navigated to the Chat component, it will get the CurrentChatId from AsyncStorage so that I can map the chat content with the firebase.
However, I got the error _this3.navigateTo is not a function with code below:
let ref = FirebaseClient.database().ref('/Chat');
ref.orderByChild("chatId").equalTo(chatId).once("value", function(snapshot) {
chatId = taskId + "_" + user1Id + "_" + user2Id;
if (snapshot.val() == null) {
ref.push({
chatId: chatId,
taskId: taskId,
user1Id: user1Id,
user2Id: user2Id,
})
}
try {
AsyncStorage.setItem("CurrentChatId", chatId).then(res => {
this.navigateTo('chat');
});
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
}
The function navigateTo is copied from the demo app of NativeBase
import { actions } from 'react-native-navigation-redux-helpers';
import { closeDrawer } from './drawer';
const {
replaceAt,
popRoute,
pushRoute,
} = actions;
export default function navigateTo(route, homeRoute) {
return (dispatch, getState) => {
const navigation = getState().cardNavigation;
const currentRouteKey = navigation.routes[navigation.routes.length - 1].key;
dispatch(closeDrawer());
if (currentRouteKey !== homeRoute && route !== homeRoute) {
dispatch(replaceAt(currentRouteKey, { key: route, index: 1 }, navigation.key));
} else if (currentRouteKey !== homeRoute && route === homeRoute) {
dispatch(popRoute(navigation.key));
} else if (currentRouteKey === homeRoute && route !== homeRoute) {
dispatch(pushRoute({ key: route, index: 1 }, navigation.key));
}
};
}
You should bind this to the function that contains the try & catch. The best practice is to add this bind the constructor of the the component:
constructor(props) {
super(props);
this.myFunctoin = this.myfuction.bind(this);
}
Finally, I solved the problem. It is really because this.navigateTo('chat'); is inside function(snapshot)
ref.orderByChild("chatId").equalTo(chatId).once("value", function(snapshot) {
chatId = taskId + "_" + user1Id + "_" + user2Id;
if (snapshot.val() == null) {
ref.push({
chatId: chatId,
taskId: taskId,
user1Id: user1Id,
user2Id: user2Id,
})
}
}
try {
AsyncStorage.setItem("CurrentChatId", chatId).then(res => {
this.navigateTo('chat');
});
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
Take it out from the function will solve the problem.
How can I get photo src, from nativescript camera module?
public takePicture() {
cameraModule.takePicture().then(function(picture) {
console.log("Result is an image source instance");
var image = new imageModule.Image();
image.imageSource = picture;
console.dir(picture);
});
}
console.dir output:
=== dump(): dumping members ===
{
"android": {
"constructor": "constructor()function () { [native code] }"
}
}
=== dump(): dumping function and properties names ===
loadFromResource()
fromResource()
loadFromFile()
fromFile()
loadFromData()
fromData()
loadFromBase64()
fromBase64()
setNativeSource()
saveToFile()
height: 480
width: 640
=== dump(): finished ===
How do I get the image src ?
I want to upload it to firebase, so i need the src.
To upload to firebase, you need to upload the image via its path:
let imgsrc = this.imageSource.fromNativeSource(data);
let path = this.utils.documentsPath(randomName);
imgsrc.saveToFile(path, this.enums.ImageFormat.png);
this.firebase.uploadFile(path).then((uploadedFile: any) => {
this.appSettings.setString("fileName", uploadedFile.name);
this.router.navigate(['/soundcloud']);
this.LoadingIndicator.hide();
}, (error: any) => {
alert("File upload error: " + error);
});
}, (err: any) => {
alert(err);
});
Figured it out, this works:
public takePicture() {
cameraModule.takePicture().then((picture) => {
var image = new imageModule.Image();
image.imageSource = picture;
let savePath = fs.knownFolders.documents().path;
let fileName = 'img_' + new Date().getTime() + '_' + this.currentUserId.getValue() + '.' + enumsModule.ImageFormat.jpeg;
let filePath = fs.path.join( savePath, fileName );
picture.saveToFile(filePath, enumsModule.ImageFormat.jpeg);
this.photoService.uploadImage(filePath, fileName).then((data) => {
this._router.navigate(["/upload", fileName, this.currentUserId.getValue()]);
});
});
}
Using the chat example, when I close the browser window, I do not ever see the #Disconnect method called.
Here is my service:-
package org.atmosphere.samples.chat;
import java.io.IOException;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ManagedService(path = "{room: [a-zA-Z][a-zA-Z_0-9]*}")
public class Chat
{
private static final Logger LOGGER = LoggerFactory.getLogger(Chat.class);
#Ready
public void onReady(final AtmosphereResource inAtmosphereResource)
{
LOGGER.info("Browser {} connected.", inAtmosphereResource.uuid());
}
#Disconnect
public void onDisconnect(AtmosphereResourceEvent event)
{
if (event.isCancelled())
{
LOGGER.info("Browser {} unexpectedly disconnected", event.getResource().uuid());
}
else if (event.isClosedByClient())
{
LOGGER.info("Browser {} closed the connection", event.getResource().uuid());
}
else
{
LOGGER.info("close event was called, but I know not why");
}
}
#org.atmosphere.config.service.Message(encoders = { JacksonEncoder.class }, decoders = { JacksonDecoder.class })
public Message onMessage(Message message) throws IOException
{
LOGGER.info("{} just send {}", message.getAuthor(), message.getMessage());
return message;
}
}
Here is my JS:-
$(function () {
"use strict";
var header = $('#header');
var content = $('#content');
var input = $('#input');
var status = $('#status');
console.log(document.location.hash);
var bookingNumber = document.location.hash.substring(1, document.location.hash.length);
console.log(bookingNumber);
var myName = false;
var author = null;
var logged = false;
var socket = $.atmosphere;
var subSocket;
var transport = 'websocket';
// We are now ready to cut the request
var request = { url: document.location.toString().replace(/#.*/, "") + 'chat/' + bookingNumber,
contentType : "application/json",
trackMessageLength : true,
shared : true,
logLevel : "debug",
transport : transport ,
fallbackTransport: 'long-polling'};
request.onOpen = function(response) {
content.html($('>p<', { text: 'Atmosphere connected using ' + response.transport }));
input.removeAttr('disabled').focus();
status.text('Choose name:');
transport = response.transport;
if (response.transport == "local") {
subSocket.pushLocal("Name?");
}
};
request.onTransportFailure = function(errorMsg, request) {
jQuery.atmosphere.info(errorMsg);
if (window.EventSource) {
request.fallbackTransport = "sse";
transport = "see";
}
header.html($('<h3>', { text: 'Atmosphere Chat. Default transport is WebSocket, fallback is ' + request.fallbackTransport }));
};
request.onMessage = function (response) {
// We need to be logged first.
if (!myName) return;
var message = response.responseBody;
try {
var json = jQuery.parseJSON(message);
} catch (e) {
console.log('This doesn\'t look like a valid JSON: ', message.data);
return;
}
if (!logged) {
logged = true;
status.text(myName + ': ').css('color', 'blue');
input.removeAttr('disabled').focus();
subSocket.pushLocal(myName);
} else {
input.removeAttr('disabled');
var me = json.author == author;
var date = typeof(json.time) == 'string' ? parseInt(json.time) : json.time;
addMessage(json.author, json.message, me ? 'blue' : 'black', new Date(date));
}
};
request.onClose = function(response) {
logged = false;
}
subSocket = socket.subscribe(request);
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (author == null) {
author = msg;
}
subSocket.push(jQuery.stringifyJSON({ author: author, message: msg }));
$(this).val('');
input.attr('disabled', 'disabled');
if (myName === false) {
myName = msg;
}
}
});
function addMessage(author, message, color, datetime) {
content.append('<p><span style="color:' + color + '">' + author + '</span> # ' +
+ (datetime.getHours() < 10 ? '0' + datetime.getHours() : datetime.getHours()) + ':'
+ (datetime.getMinutes() < 10 ? '0' + datetime.getMinutes() : datetime.getMinutes())
+ ': ' + message + '</p>');
}
});
I am running on glassfish 3, with web sockets and comet enabled.
UPDATE I forgot to mention that I am using v 2.1.0RC1
tested with GF 3.1.2.2:
Blockquote
[#|2013-12-02T10:43:24.743-0500|INFO|glassfish3.1.2|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=25;_ThreadName=http-thread-pool-8080(4);|10:43:24.743 [http-thread-pool-8080(4)] INFO org.atmosphere.samples.chat.Chat - Browser 0f7f5596-bd25-4dda-a488-1357da8487f5 closed the connection
Let's havce the discussion on the Atmosphere ML in case you are still experiencing issue. I've tested with the sample BTW.
-- Jeanfrancois