make video conference work
This commit is contained in:
parent
9ea1a6f3d6
commit
1e89e52b12
5
.gitignore
vendored
5
.gitignore
vendored
@ -360,4 +360,7 @@ MigrationBackup/
|
|||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
/EnotaryoPH/EnotaryoPH.Web/wwwroot/dist/
|
|
||||||
|
|
||||||
|
.parcel*/
|
||||||
|
/EnotaryoPH/EnotaryoPH.Web/wwwroot/dist
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
namespace EnotaryoPH.Data.Constants
|
||||||
|
{
|
||||||
|
public enum VideoConferenceStatus
|
||||||
|
{
|
||||||
|
New = 0,
|
||||||
|
InProgress = 1,
|
||||||
|
Expired = 49,
|
||||||
|
Abandoned = 50,
|
||||||
|
Completed = 100
|
||||||
|
}
|
||||||
|
}
|
@ -5,23 +5,37 @@ namespace EnotaryoPH.Data.Entities
|
|||||||
[Table("Lawyers")]
|
[Table("Lawyers")]
|
||||||
public class Lawyer
|
public class Lawyer
|
||||||
{
|
{
|
||||||
[Column("LawyerID")]
|
[Column("CommissionExpiration")]
|
||||||
public int LawyerID { get; set; }
|
public DateTime? CommissionExpiration { get; set; }
|
||||||
|
|
||||||
[Column("Rollnumber")]
|
[Column("CommissionLocation")]
|
||||||
public string? Rollnumber { get; set; }
|
public string? CommissionLocation { get; set; }
|
||||||
|
|
||||||
|
[Column("CommissionNumber")]
|
||||||
|
public string? CommissionNumber { get; set; }
|
||||||
|
|
||||||
|
[Column("CreatedOn")]
|
||||||
|
public DateTime? CreatedOn { get; set; }
|
||||||
|
|
||||||
[Column("IBPNumber")]
|
[Column("IBPNumber")]
|
||||||
public string? IBPNumber { get; set; }
|
public string? IBPNumber { get; set; }
|
||||||
|
|
||||||
|
[Column("Lawyer_UID")]
|
||||||
|
public Guid? Lawyer_UID { get; set; }
|
||||||
|
|
||||||
|
[Column("LawyerID")]
|
||||||
|
public int LawyerID { get; set; }
|
||||||
|
|
||||||
|
public List<LawyerVideoConferenceSchedule> LawyerVideoConferenceSchedules { get; set; }
|
||||||
|
|
||||||
[Column("MCLEComplianceNumber")]
|
[Column("MCLEComplianceNumber")]
|
||||||
public string? MCLEComplianceNumber { get; set; }
|
public string? MCLEComplianceNumber { get; set; }
|
||||||
|
|
||||||
[Column("MCLEDate")]
|
[Column("MCLEDate")]
|
||||||
public DateTime? MCLEDate { get; set; }
|
public DateTime? MCLEDate { get; set; }
|
||||||
|
|
||||||
[Column("PTRNumber")]
|
[Column("OfficeAddress")]
|
||||||
public string? PTRNumber { get; set; }
|
public string? OfficeAddress { get; set; }
|
||||||
|
|
||||||
[Column("PTRDate")]
|
[Column("PTRDate")]
|
||||||
public DateTime? PTRDate { get; set; }
|
public DateTime? PTRDate { get; set; }
|
||||||
@ -29,31 +43,19 @@ namespace EnotaryoPH.Data.Entities
|
|||||||
[Column("PTRlocation")]
|
[Column("PTRlocation")]
|
||||||
public string? PTRlocation { get; set; }
|
public string? PTRlocation { get; set; }
|
||||||
|
|
||||||
[Column("OfficeAddress")]
|
[Column("PTRNumber")]
|
||||||
public string? OfficeAddress { get; set; }
|
public string? PTRNumber { get; set; }
|
||||||
|
|
||||||
[Column("UserID")]
|
[Column("Rollnumber")]
|
||||||
public int UserID { get; set; }
|
public string? Rollnumber { get; set; }
|
||||||
|
|
||||||
[Column("Lawyer_UID")]
|
|
||||||
public Guid? Lawyer_UID { get; set; }
|
|
||||||
|
|
||||||
[Column("CreatedOn")]
|
|
||||||
public DateTime? CreatedOn { get; set; }
|
|
||||||
|
|
||||||
[Column("CommissionNumber")]
|
|
||||||
public string? CommissionNumber { get; set; }
|
|
||||||
|
|
||||||
[Column("CommissionLocation")]
|
|
||||||
public string? CommissionLocation { get; set; }
|
|
||||||
|
|
||||||
[Column("CommissionExpiration")]
|
|
||||||
public DateTime? CommissionExpiration { get; set; }
|
|
||||||
|
|
||||||
[Column("Status")]
|
[Column("Status")]
|
||||||
public string? Status { get; set; }
|
public string? Status { get; set; }
|
||||||
|
|
||||||
[ForeignKey("UserID")]
|
[ForeignKey("UserID")]
|
||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
|
|
||||||
|
[Column("UserID")]
|
||||||
|
public int UserID { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,28 +5,34 @@ namespace EnotaryoPH.Data.Entities
|
|||||||
[Table("LawyerVideoConferenceParticipants")]
|
[Table("LawyerVideoConferenceParticipants")]
|
||||||
public class LawyerVideoConferenceParticipant
|
public class LawyerVideoConferenceParticipant
|
||||||
{
|
{
|
||||||
[Column("LawyerVideoConferenceParticipantID")]
|
|
||||||
public int LawyerVideoConferenceParticipantID { get; set; }
|
|
||||||
|
|
||||||
[Column("LawyerVideoConferenceScheduleID")]
|
|
||||||
public int LawyerVideoConferenceScheduleID { get; set; }
|
|
||||||
|
|
||||||
[Column("ParticipantID")]
|
|
||||||
public int? ParticipantID { get; set; }
|
|
||||||
|
|
||||||
[Column("Status")]
|
|
||||||
public string? Status { get; set; }
|
|
||||||
|
|
||||||
[Column("CreatedOn")]
|
[Column("CreatedOn")]
|
||||||
public DateTime? CreatedOn { get; set; }
|
public DateTime? CreatedOn { get; set; }
|
||||||
|
|
||||||
[Column("LawyerVideoConferenceParticipant_UID")]
|
[Column("LawyerVideoConferenceParticipant_UID")]
|
||||||
public Guid? LawyerVideoConferenceParticipant_UID { get; set; }
|
public Guid? LawyerVideoConferenceParticipant_UID { get; set; }
|
||||||
|
|
||||||
[ForeignKey("ParticipantID")]
|
[Column("LawyerVideoConferenceParticipantID")]
|
||||||
public User Participant { get; set; }
|
public int LawyerVideoConferenceParticipantID { get; set; }
|
||||||
|
|
||||||
[ForeignKey("LawyerVideoConferenceScheduleID")]
|
[ForeignKey("LawyerVideoConferenceScheduleID")]
|
||||||
public LawyerVideoConferenceSchedule LawyerVideoConferenceSchedule { get; set; }
|
public LawyerVideoConferenceSchedule LawyerVideoConferenceSchedule { get; set; }
|
||||||
|
|
||||||
|
[Column("LawyerVideoConferenceScheduleID")]
|
||||||
|
public int LawyerVideoConferenceScheduleID { get; set; }
|
||||||
|
|
||||||
|
[Column("MeetingRoomTokenID")]
|
||||||
|
public string? MeetingRoomTokenID { get; set; }
|
||||||
|
|
||||||
|
[Column("MeetingRoomUserID")]
|
||||||
|
public string? MeetingRoomUserID { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey("ParticipantID")]
|
||||||
|
public User Participant { get; set; }
|
||||||
|
|
||||||
|
[Column("ParticipantID")]
|
||||||
|
public int? ParticipantID { get; set; }
|
||||||
|
|
||||||
|
[Column("Status")]
|
||||||
|
public string? Status { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,27 +5,39 @@ namespace EnotaryoPH.Data.Entities
|
|||||||
[Table("LawyerVideoConferenceSchedule")]
|
[Table("LawyerVideoConferenceSchedule")]
|
||||||
public class LawyerVideoConferenceSchedule
|
public class LawyerVideoConferenceSchedule
|
||||||
{
|
{
|
||||||
[Column("LawyerVideoConferenceScheduleID")]
|
[Column("CreatedOn")]
|
||||||
public int LawyerVideoConferenceScheduleID { get; set; }
|
public DateTime? CreatedOn { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey("LawyerID")]
|
||||||
|
public Lawyer Lawyer { get; set; }
|
||||||
|
|
||||||
[Column("LawyerID")]
|
[Column("LawyerID")]
|
||||||
public int LawyerID { get; set; }
|
public int LawyerID { get; set; }
|
||||||
|
|
||||||
[Column("TransactionID")]
|
public List<LawyerVideoConferenceParticipant> LawyerVideoConferenceParticipants { get; set; }
|
||||||
public int TransactionID { get; set; }
|
|
||||||
|
|
||||||
[Column("MeetingDate")]
|
|
||||||
public DateTime MeetingDate { get; set; }
|
|
||||||
|
|
||||||
[Column("CreatedOn")]
|
|
||||||
public DateTime? CreatedOn { get; set; }
|
|
||||||
|
|
||||||
[Column("LawyerVideoConferenceSchedule_UID")]
|
[Column("LawyerVideoConferenceSchedule_UID")]
|
||||||
public Guid LawyerVideoConferenceSchedule_UID { get; set; }
|
public Guid LawyerVideoConferenceSchedule_UID { get; set; }
|
||||||
|
|
||||||
|
[Column("LawyerVideoConferenceScheduleID")]
|
||||||
|
public int LawyerVideoConferenceScheduleID { get; set; }
|
||||||
|
|
||||||
|
[Column("MeetingDate")]
|
||||||
|
public DateTime MeetingDate { get; set; }
|
||||||
|
|
||||||
|
[Column("MeetingRoomID")]
|
||||||
|
public string? MeetingRoomID { get; set; }
|
||||||
|
|
||||||
|
[Column("MeetingRoomTokenID")]
|
||||||
|
public string? MeetingRoomTokenID { get; set; }
|
||||||
|
|
||||||
|
[Column("MeetingRoomUserID")]
|
||||||
|
public string? MeetingRoomUserID { get; set; }
|
||||||
|
|
||||||
[Column("Status")]
|
[Column("Status")]
|
||||||
public string? Status { get; set; }
|
public string? Status { get; set; }
|
||||||
|
|
||||||
public List<LawyerVideoConferenceParticipant> LawyerVideoConferenceParticipants { get; set; }
|
[Column("TransactionID")]
|
||||||
|
public int TransactionID { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,213 @@
|
|||||||
|
import { AzureCommunicationTokenCredential } from '@azure/communication-common';
|
||||||
|
import { CallClient, LocalVideoStream, VideoStreamRenderer } from '@azure/communication-calling';
|
||||||
|
|
||||||
|
class VideoCall {
|
||||||
|
constructor() {
|
||||||
|
this.callClient = null;
|
||||||
|
this.callAgent = null;
|
||||||
|
this.deviceManager = null;
|
||||||
|
this.localVideoStream = null;
|
||||||
|
this.call = null;
|
||||||
|
this.videoElement = document.createElement('video');
|
||||||
|
this.videoElement.setAttribute('autoplay', '');
|
||||||
|
this.videoElement.setAttribute('muted', '');
|
||||||
|
this.stateChangedCallback = null;
|
||||||
|
this.remoteParticipantsUpdated = null;
|
||||||
|
this.isLocalVideoStartedChanged = null;
|
||||||
|
this.localVideoStreamsUpdated = null;
|
||||||
|
this.idChanged = null;
|
||||||
|
this.onCreateLocalVideoStream = null;
|
||||||
|
this.remoteParticipantStateChanged = null;
|
||||||
|
this.remoteVideoIsAvailableChanged = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(userAccessToken, options) {
|
||||||
|
this.stateChangedCallback = options.stateChangedCallback;
|
||||||
|
this.remoteParticipantsUpdated = options.remoteParticipantsUpdated;
|
||||||
|
this.isLocalVideoStartedChanged = options.isLocalVideoStartedChanged;
|
||||||
|
this.localVideoStreamsUpdated = options.localVideoStreamsUpdated;
|
||||||
|
this.idChanged = options.idChanged;
|
||||||
|
this.onCreateLocalVideoStream = options.onCreateLocalVideoStream;
|
||||||
|
this.remoteParticipantStateChanged = options.remoteParticipantStateChanged;
|
||||||
|
this.remoteVideoIsAvailableChanged = options.remoteVideoIsAvailableChanged;
|
||||||
|
|
||||||
|
const tokenCredential = new AzureCommunicationTokenCredential(userAccessToken);
|
||||||
|
|
||||||
|
this.callClient = new CallClient();
|
||||||
|
this.callAgent = await this.callClient.createCallAgent(tokenCredential);
|
||||||
|
|
||||||
|
this.deviceManager = await this.callClient.getDeviceManager();
|
||||||
|
await this.deviceManager.askDevicePermission({ audio: true, video: true });
|
||||||
|
const cameras = await this.deviceManager.getCameras();
|
||||||
|
this.localVideoStream = new LocalVideoStream(cameras[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async startLocalVideo() {
|
||||||
|
if (this.localVideoStream) {
|
||||||
|
//const localVideoOptions = new VideoStreamOptions();
|
||||||
|
await this.callAgent.startCall([this.callAgent.identity], { video: [this.localVideoStream] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopLocalVideo() {
|
||||||
|
if (this.call) {
|
||||||
|
this.call.stopVideo(this.localVideoStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async joinRoom(roomId) {
|
||||||
|
// Assuming you have a room ID and call the appropriate ACS API to join a room
|
||||||
|
// This is just a placeholder, replace with actual logic
|
||||||
|
console.log('Joining room:', roomId);
|
||||||
|
if (this.callAgent) {
|
||||||
|
try {
|
||||||
|
const localVideoStream = await this.createLocalVideoStream();
|
||||||
|
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
|
||||||
|
|
||||||
|
const roomCallLocator = { roomId: roomId };
|
||||||
|
let call = this.callAgent.join(roomCallLocator, { videoOptions });
|
||||||
|
this.subscribeToCall(call);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribeToCall(call) {
|
||||||
|
try {
|
||||||
|
call.on('idChanged', () => {
|
||||||
|
this.idChanged?.(call.id);
|
||||||
|
});
|
||||||
|
call.on('stateChanged', async () => {
|
||||||
|
this.stateChangedCallback?.(call.state);
|
||||||
|
});
|
||||||
|
call.on('isLocalVideoStartedChanged', () => {
|
||||||
|
this.isLocalVideoStartedChanged?.(call.isLocalVideoStarted);
|
||||||
|
});
|
||||||
|
call.on('localVideoStreamsUpdated', e => {
|
||||||
|
this.localVideoStreamsUpdated?.(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to the call's 'remoteParticipantsUpdated' event to be
|
||||||
|
// notified when new participants are added to the call or removed from the call.
|
||||||
|
call.on('remoteParticipantsUpdated', e => {
|
||||||
|
this.remoteParticipantsUpdated?.(e);
|
||||||
|
|
||||||
|
e.added.forEach(remoteParticipant => {
|
||||||
|
this.subscribeToRemoteParticipant(remoteParticipant)
|
||||||
|
});
|
||||||
|
// Unsubscribe from participants that are removed from the call
|
||||||
|
e.removed.forEach(remoteParticipant => {
|
||||||
|
console.log('Remote participant removed from the call.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
call.localVideoStreams.forEach(async (lvs) => {
|
||||||
|
this.localVideoStream = lvs;
|
||||||
|
await this.displayLocalVideoStream(lvs);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inspect the call's current remote participants and subscribe to them.
|
||||||
|
call.remoteParticipants.forEach(remoteParticipant => {
|
||||||
|
this.subscribeToRemoteParticipant(remoteParticipant);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeToRemoteParticipant(remoteParticipant) {
|
||||||
|
try {
|
||||||
|
// Inspect the initial remoteParticipant.state value.
|
||||||
|
console.log(`Remote participant state: ${remoteParticipant.state}`);
|
||||||
|
// Subscribe to remoteParticipant's 'stateChanged' event for value changes.
|
||||||
|
remoteParticipant.on('stateChanged', () => {
|
||||||
|
console.log(`Remote participant state changed: ${remoteParticipant.state}`, JSON.stringify(remoteParticipant));
|
||||||
|
this.remoteParticipantStateChanged?.(remoteParticipant.state);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inspect the remoteParticipants's current videoStreams and subscribe to them.
|
||||||
|
remoteParticipant.videoStreams.forEach(remoteVideoStream => {
|
||||||
|
this.subscribeToRemoteVideoStream(remoteVideoStream);
|
||||||
|
});
|
||||||
|
// Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be
|
||||||
|
// notified when the remoteParticipant adds new videoStreams and removes video streams.
|
||||||
|
remoteParticipant.on('videoStreamsUpdated', e => {
|
||||||
|
// Subscribe to new remote participant's video streams that were added.
|
||||||
|
e.added.forEach(remoteVideoStream => {
|
||||||
|
this.subscribeToRemoteVideoStream(remoteVideoStream);
|
||||||
|
});
|
||||||
|
// Unsubscribe from remote participant's video streams that were removed.
|
||||||
|
e.removed.forEach(remoteVideoStream => {
|
||||||
|
console.log('Remote participant video stream was removed.');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribeToRemoteVideoStream(remoteVideoStream) {
|
||||||
|
let renderer = new VideoStreamRenderer(remoteVideoStream);
|
||||||
|
let view;
|
||||||
|
|
||||||
|
const createView = async () => {
|
||||||
|
// Create a renderer view for the remote video stream.
|
||||||
|
view = await renderer.createView();
|
||||||
|
this.remoteVideoIsAvailableChanged?.({
|
||||||
|
isAvailable: remoteVideoStream.isAvailable,
|
||||||
|
participantId: remoteVideoStream.tsParticipantId,
|
||||||
|
el: view.target
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remote participant has switched video on/off
|
||||||
|
remoteVideoStream.on('isAvailableChanged', async () => {
|
||||||
|
try {
|
||||||
|
if (remoteVideoStream.isAvailable) {
|
||||||
|
await createView();
|
||||||
|
} else {
|
||||||
|
view?.dispose();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remote participant has video on initially.
|
||||||
|
if (remoteVideoStream.isAvailable) {
|
||||||
|
try {
|
||||||
|
await createView();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createLocalVideoStream() {
|
||||||
|
const camera = (await this.deviceManager.getCameras())[0];
|
||||||
|
if (camera) {
|
||||||
|
return new LocalVideoStream(camera);
|
||||||
|
} else {
|
||||||
|
console.error(`No camera device found on the system`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveRoom() {
|
||||||
|
if (this.call) {
|
||||||
|
this.call.hangUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async displayLocalVideoStream(lvs) {
|
||||||
|
try {
|
||||||
|
let localVideoStreamRenderer = new VideoStreamRenderer(lvs);
|
||||||
|
const view = await localVideoStreamRenderer.createView();
|
||||||
|
this.onCreateLocalVideoStream?.(view.target);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VideoCall;
|
@ -1,40 +1,38 @@
|
|||||||
jfa.components.dialog = (function () {
|
function _confirm({ message, title, callbackyes, callbackno, yesLabel, noLabel }) {
|
||||||
function _confirm({ message, title, callbackyes, callbackno, yesLabel, noLabel }) {
|
const dialogTemplate = jfa.page.getDialogTemplate().content;
|
||||||
const dialogTemplate = jfa.page.getDialogTemplate().content;
|
|
||||||
|
|
||||||
let spanTitle = dialogTemplate.querySelector(".modal__title__text");
|
let spanTitle = dialogTemplate.querySelector(".modal__title__text");
|
||||||
spanTitle.textContent = title || "Confirm";
|
spanTitle.textContent = title || "Confirm";
|
||||||
|
|
||||||
let bodyMessage = dialogTemplate.querySelector(".modal__body-message");
|
let bodyMessage = dialogTemplate.querySelector(".modal__body-message");
|
||||||
bodyMessage.textContent = message;
|
bodyMessage.textContent = message;
|
||||||
|
|
||||||
const dialog = dialogTemplate.querySelector("#dialog-container");
|
const dialog = dialogTemplate.querySelector("#dialog-container");
|
||||||
const modal = bootstrap.Modal.getOrCreateInstance(dialog);
|
const modal = bootstrap.Modal.getOrCreateInstance(dialog);
|
||||||
|
|
||||||
let buttonNo = dialogTemplate.querySelector(".modal__footer__button-no");
|
let buttonNo = dialogTemplate.querySelector(".modal__footer__button-no");
|
||||||
if (noLabel) {
|
if (noLabel) {
|
||||||
buttonNo.textContent = noLabel;
|
buttonNo.textContent = noLabel;
|
||||||
}
|
|
||||||
buttonNo.addEventListener("click", function () {
|
|
||||||
buttonNo.blur();
|
|
||||||
modal.hide();
|
|
||||||
callbackno?.();
|
|
||||||
});
|
|
||||||
|
|
||||||
let buttonYes = dialogTemplate.querySelector(".modal__footer__button-yes");
|
|
||||||
if (yesLabel) {
|
|
||||||
buttonYes.textContent = yesLabel;
|
|
||||||
}
|
|
||||||
buttonYes.addEventListener("click", function () {
|
|
||||||
buttonYes.blur();
|
|
||||||
modal.hide();
|
|
||||||
callbackyes?.();
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.show();
|
|
||||||
}
|
}
|
||||||
|
buttonNo.addEventListener("click", function () {
|
||||||
|
buttonNo.blur();
|
||||||
|
modal.hide();
|
||||||
|
callbackno?.();
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
let buttonYes = dialogTemplate.querySelector(".modal__footer__button-yes");
|
||||||
confirm: _confirm
|
if (yesLabel) {
|
||||||
};
|
buttonYes.textContent = yesLabel;
|
||||||
})();
|
}
|
||||||
|
buttonYes.addEventListener("click", function () {
|
||||||
|
buttonYes.blur();
|
||||||
|
modal.hide();
|
||||||
|
callbackyes?.();
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
confirm: _confirm
|
||||||
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
jfa.page = (function () {
|
function _getAlertContainer() {
|
||||||
function _getAlertContainer() {
|
|
||||||
return document.getElementById("ContainerAlert");
|
return document.getElementById("ContainerAlert");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,14 +20,15 @@
|
|||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const page = {
|
||||||
getAlertContainer: _getAlertContainer,
|
getAlertContainer: _getAlertContainer,
|
||||||
getAlertTemplate: _getAlertTemplate,
|
getAlertTemplate: _getAlertTemplate,
|
||||||
getAntiForgeryToken: _getAntiForgeryToken,
|
getAntiForgeryToken: _getAntiForgeryToken,
|
||||||
getDialogContainer: _getDialogContainer,
|
getDialogContainer: _getDialogContainer,
|
||||||
getDialogTemplate: _getDialogTemplate,
|
getDialogTemplate: _getDialogTemplate,
|
||||||
reload: reload
|
reload: reload
|
||||||
};
|
}
|
||||||
})();
|
|
||||||
|
export default { page };
|
@ -1,37 +1,36 @@
|
|||||||
jfa.utilities.element = (function () {
|
|
||||||
function disable(element) {
|
function disable(element) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
element.disabled = true;
|
element.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable(element) {
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide(element) {
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function show(element, suppressEvent) {
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function enable(element) {
|
element.hidden = false;
|
||||||
if (!element) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
element.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide(element) {
|
export default {
|
||||||
if (!element) {
|
disable: disable,
|
||||||
return;
|
enable: enable,
|
||||||
}
|
hide: hide,
|
||||||
element.hidden = true;
|
show: show
|
||||||
}
|
};
|
||||||
|
|
||||||
function show(element, suppressEvent) {
|
|
||||||
if (!element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
element.hidden = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
disable: disable,
|
|
||||||
enable: enable,
|
|
||||||
hide: hide,
|
|
||||||
show: show
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
@ -1,28 +1,26 @@
|
|||||||
jfa.utilities.request = (function () {
|
|
||||||
|
async function del(url, data) {
|
||||||
|
return await _fetch(url, data, "DELETE");
|
||||||
|
}
|
||||||
|
|
||||||
async function del(url, data) {
|
async function _fetch(url, data, method) {
|
||||||
return await _fetch(url, data, "DELETE");
|
const response = await fetch(url, {
|
||||||
}
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'RequestVerificationToken': jfa.page.getAntiForgeryToken()
|
||||||
|
},
|
||||||
|
body: data ? JSON.stringify(data) : data
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
async function _fetch(url, data, method) {
|
async function post(url, data) {
|
||||||
const response = await fetch(url, {
|
return await _fetch(url, data, "POST");
|
||||||
method: method,
|
}
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'RequestVerificationToken': jfa.page.getAntiForgeryToken()
|
|
||||||
},
|
|
||||||
body: data ? JSON.stringify(data) : data
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function post(url, data) {
|
export default {
|
||||||
return await _fetch(url, data, "POST");
|
delete: del,
|
||||||
}
|
post: post
|
||||||
|
};
|
||||||
return {
|
|
||||||
delete: del,
|
|
||||||
post: post
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
jfa.utilities.routing = (function () {
|
function getCurrentURL() {
|
||||||
function getCurrentURL() {
|
return new URL(window.location.origin + window.location.pathname);
|
||||||
return new URL(window.location.origin + window.location.pathname);
|
}
|
||||||
}
|
|
||||||
|
function getCurrentURLWithHandler(handler) {
|
||||||
|
let url = getCurrentURL();
|
||||||
|
url.searchParams.append("handler", handler);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getCurrentURL: getCurrentURL,
|
||||||
|
getCurrentURLWithHandler: getCurrentURLWithHandler
|
||||||
|
}
|
||||||
|
|
||||||
function getCurrentURLWithHandler(handler) {
|
|
||||||
let url = getCurrentURL();
|
|
||||||
url.searchParams.append("handler", handler);
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
getCurrentURL: getCurrentURL,
|
|
||||||
getCurrentURLWithHandler: getCurrentURLWithHandler
|
|
||||||
};
|
|
||||||
})();
|
|
@ -1,5 +1,25 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
var jfa = {
|
|
||||||
components: {},
|
import page from "../../Assets/js/Page/_Page.js";
|
||||||
utilities: {}
|
import routing from "../../Assets/js/Utilities/Routing/_Routing.js";
|
||||||
|
import element from "../../Assets/js/Utilities/Element/_Element.js";
|
||||||
|
import request from "../../Assets/js/Utilities/Request/_Request.js";
|
||||||
|
import dialog from "../../Assets/js/Components/Dialog/_Dialog.js";
|
||||||
|
import videocall from "../../Assets/js/Communication/VideoCall/_VideoCall.js";
|
||||||
|
|
||||||
|
const jfa = {
|
||||||
|
components: {
|
||||||
|
dialog
|
||||||
|
},
|
||||||
|
utilities: {
|
||||||
|
routing,
|
||||||
|
element,
|
||||||
|
request
|
||||||
|
},
|
||||||
|
page,
|
||||||
|
communication: {
|
||||||
|
videocall: new videocall()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
export default { jfa };
|
||||||
|
window.jfa = jfa;
|
@ -8,6 +8,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Communication.Identity" Version="1.3.1" />
|
||||||
|
<PackageReference Include="Azure.Communication.Rooms" Version="1.1.1" />
|
||||||
<PackageReference Include="CompreFace.NET.Sdk" Version="1.0.2" />
|
<PackageReference Include="CompreFace.NET.Sdk" Version="1.0.2" />
|
||||||
<PackageReference Include="Coravel" Version="6.0.2" />
|
<PackageReference Include="Coravel" Version="6.0.2" />
|
||||||
<PackageReference Include="Coravel.Mailer" Version="7.1.0" />
|
<PackageReference Include="Coravel.Mailer" Version="7.1.0" />
|
||||||
@ -30,5 +32,7 @@
|
|||||||
<Folder Include="wwwroot\lib\signalr\" />
|
<Folder Include="wwwroot\lib\signalr\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="ParcelBuild" BeforeTargets="Build">
|
||||||
|
<Exec Command="npm run build" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -2,3 +2,60 @@
|
|||||||
@model EnotaryoPH.Web.Pages.Notary.Dashboard.DashboardModel
|
@model EnotaryoPH.Web.Pages.Notary.Dashboard.DashboardModel
|
||||||
@{
|
@{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section Head {
|
||||||
|
<link href="~/lib/fontawesome-free-6.7.1-web/css/all.min.css" rel="stylesheet" />
|
||||||
|
}
|
||||||
|
|
||||||
|
<section class="my-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2 col-sm-1 col-md-3 g-0">
|
||||||
|
<div class="sidemenu">
|
||||||
|
<div class="align-items-center sidemenu__menuitem"><a class="d-flex flex-grow-1 justify-content-center align-items-center justify-content-md-start p-1 text-decoration-none" href="#available-transactions"><i class="far fa-check-circle fs-4 text-success sidemenu__menuitem__icon" style="padding: 5px;"></i><span class="d-none d-md-inline-block ms-1 sidemenu__menuitem__text">Available</span></a></div>
|
||||||
|
<div class="align-items-center sidemenu__menuitem"><a class="d-flex flex-grow-1 justify-content-center align-items-center justify-content-md-start p-1 text-decoration-none" href="#incomplete-docs"><i class="far fa-clock fs-4 text-warning sidemenu__menuitem__icon" style="padding: 5px;"></i><span class="d-none d-md-inline-block ms-1 sidemenu__menuitem__text">Incomplete</span></a></div>
|
||||||
|
<div class="align-items-center sidemenu__menuitem"><a class="d-flex flex-grow-1 justify-content-center align-items-center justify-content-md-start p-1 text-decoration-none" href="#identification-docs"><i class="far fa-address-card fs-4 text-dark sidemenu__menuitem__icon" style="padding: 5px;"></i><span class="d-none d-md-inline-block ms-1 sidemenu__menuitem__text">Identification Docs</span></a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col g-0 mx-2">
|
||||||
|
<div id="available-transactions" class="row g-0 mb-5">
|
||||||
|
<div class="col">
|
||||||
|
<h3>Available Transactions</h3>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Link</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@* @if (Model.CompletedDocuments.Count > 0)
|
||||||
|
{
|
||||||
|
@foreach (var item in Model.CompletedDocuments)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@item.Type</td>
|
||||||
|
<td>@item.Date.ToShortDateString()</td>
|
||||||
|
<td><a href="@item.Link">View</a></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
No records to display.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
} *@
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
@ -33,7 +33,7 @@ namespace EnotaryoPH.Web.Pages.Participant.Registration
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingUser = _notaryoDBContext.Users.FirstOrDefault(e => e.Email.ToLower() == signatory.Email.ToLower());
|
var existingUser = _notaryoDBContext.Users.FirstOrDefault(e => e.Email.Equals(signatory.Email, StringComparison.CurrentCultureIgnoreCase));
|
||||||
if (existingUser != null)
|
if (existingUser != null)
|
||||||
{
|
{
|
||||||
signatory.Status = nameof(SignatoryStatus.Registered);
|
signatory.Status = nameof(SignatoryStatus.Registered);
|
||||||
|
@ -1,4 +1,160 @@
|
|||||||
@page
|
@page "{Transaction_UID}"
|
||||||
|
@using System.Text.Json
|
||||||
@model EnotaryoPH.Web.Pages.Participant.VideoCall.RoomModel
|
@model EnotaryoPH.Web.Pages.Participant.VideoCall.RoomModel
|
||||||
@{
|
@{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section Head {
|
||||||
|
<style>
|
||||||
|
.video-container {
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 56.25%; /* 16:9 aspect ratio */
|
||||||
|
background: #000;
|
||||||
|
border: 2px solid #444;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container video {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover; /* This will maintain the video's original aspect ratio and crop if necessary */
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-element {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participant-name {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
color: white;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 50%;
|
||||||
|
/* transform: translateX(-50%); */
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participant-col {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
display:flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container-fluid py-3" id="videoGrid-container">
|
||||||
|
<div class="row g-2" id="videoGrid">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template id="templateVideo">
|
||||||
|
<div class="video-container bg-light">
|
||||||
|
<div class="participant-name">Participant Name</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<input type="hidden" value="@JsonSerializer.Serialize(Model.Participants)" />
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<script type="text/javascript" src="~/dist/js/_Jfa.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
let participants = JSON.parse('@Html.Raw(JsonSerializer.Serialize(Model.Participants))');
|
||||||
|
function updateGrid() {
|
||||||
|
const videoGrid = document.getElementById('videoGrid');
|
||||||
|
videoGrid.innerHTML = '';
|
||||||
|
|
||||||
|
participants.forEach((participant, index) => {
|
||||||
|
const col = document.createElement('div');
|
||||||
|
col.className = 'participant-col';
|
||||||
|
var tmpl = document.getElementById("templateVideo").cloneNode(true).content;
|
||||||
|
let vidcontainer = tmpl.querySelector(".video-container")
|
||||||
|
if (vidcontainer) {
|
||||||
|
vidcontainer.id = participant.Id;
|
||||||
|
vidcontainer.classList.add(participant.Id);
|
||||||
|
vidcontainer.classList.add(participant.Type == 'Notary' ? 'local-video-container' : 'remote-video-container');
|
||||||
|
}
|
||||||
|
let participantName = tmpl.querySelector(".participant-name")
|
||||||
|
if (participantName) {
|
||||||
|
participantName.textContent = participant.DisplayName;
|
||||||
|
}
|
||||||
|
col.appendChild(tmpl);
|
||||||
|
videoGrid.appendChild(col);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dynamically adjust grid columns based on participant count
|
||||||
|
const count = participants.length;
|
||||||
|
const cols = count <= 2 ? 'col-12 col-sm-12 offset-md-1 col-md-10 offset-lg-0 col-lg-6' :
|
||||||
|
count <= 4 ? 'col-6 col-sm-6 col-md-6 col-lg-6 col-xl-6' :
|
||||||
|
count <= 8 ? 'col-6 col-md-6 col-lg-4 col-xl-4' :
|
||||||
|
count <= 9 ? 'col-4' :
|
||||||
|
'col-6 col-sm-4 col-lg-3';
|
||||||
|
|
||||||
|
document.querySelectorAll('.participant-col').forEach(el => {
|
||||||
|
el.className = `participant-col ${cols}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const fluid = count <= 2 ? 'container-fluid' :
|
||||||
|
count <= 8 ? 'container-xxl' :
|
||||||
|
'container-xl'
|
||||||
|
document.getElementById('videoGrid-container').className = `${fluid} py-3`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initVideoCall() {
|
||||||
|
let userAccessToken = '@Model.CommunicationUserToken';
|
||||||
|
const videoCall = jfa.communication.videocall;
|
||||||
|
let options = {
|
||||||
|
onCreateLocalVideoStream: function(el) {
|
||||||
|
if (el) {
|
||||||
|
el.style['transform'] = '';
|
||||||
|
let notary = participants.find(p => p.RoomUserID == '@Model.CommunicationUserId');
|
||||||
|
document.getElementById(notary.Id).appendChild(el);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remoteVideoIsAvailableChanged: function(e) {
|
||||||
|
let participant = participants.find(p => p.RoomUserID == e.participantId);
|
||||||
|
if (participant) {
|
||||||
|
e.el.querySelector('video').style['object-fit'] = 'cover';
|
||||||
|
document.getElementById(participant.Id).appendChild(e.el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await videoCall.init(userAccessToken, options);
|
||||||
|
videoCall.joinRoom('@Model.CommunicationRoomId');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
updateGrid();
|
||||||
|
window.addEventListener('resize', updateGrid);
|
||||||
|
|
||||||
|
await initVideoCall();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
using Azure;
|
||||||
|
using Azure.Communication;
|
||||||
|
using Azure.Communication.Identity;
|
||||||
|
using Azure.Communication.Rooms;
|
||||||
|
using EnotaryoPH.Data;
|
||||||
|
using EnotaryoPH.Data.Entities;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
@ -5,8 +11,138 @@ namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
|||||||
{
|
{
|
||||||
public class RoomModel : PageModel
|
public class RoomModel : PageModel
|
||||||
{
|
{
|
||||||
public void OnGet()
|
private const string CONNECTION_STRING = "endpoint=https://comm-enotario.asiapacific.communication.azure.com/;accesskey=yqxq5mRByG8aPwZBiT/KFfx6Rr16RR280dC3GVkGaFVNXGk91sIDy04j5jrZfjDZAvdn0fjtyF0kGJPXMGgfKg==";
|
||||||
|
private readonly ICurrentUserService _currentUserService;
|
||||||
|
private readonly NotaryoDBContext _dbContext;
|
||||||
|
private readonly ISession _session;
|
||||||
|
private readonly CommunicationUserIdentifier? _user1;
|
||||||
|
private readonly CommunicationUserIdentifier? _user2;
|
||||||
|
private CommunicationIdentityClient? _communicationIdentityClient;
|
||||||
|
private LawyerVideoConferenceSchedule _LawyerVideoConferenceSchedule;
|
||||||
|
private Transaction _Transaction;
|
||||||
|
|
||||||
|
public RoomModel(IHttpContextAccessor httpContextAccessor, ICurrentUserService currentUserService, NotaryoDBContext dbContext)
|
||||||
{
|
{
|
||||||
|
_session = httpContextAccessor.HttpContext.Session;
|
||||||
|
_currentUserService = currentUserService;
|
||||||
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGetAsync()
|
||||||
|
{
|
||||||
|
_Transaction = _dbContext.Transactions
|
||||||
|
.Include(t => t.TransactionSignatories)
|
||||||
|
.FirstOrDefault(t => t.Transaction_UID == Transaction_UID);
|
||||||
|
if (_Transaction == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
_LawyerVideoConferenceSchedule = _dbContext.LawyerVideoConferenceSchedules
|
||||||
|
.Include(meeting => meeting.LawyerVideoConferenceParticipants)
|
||||||
|
.ThenInclude(p => p.Participant)
|
||||||
|
.Include(meeting => meeting.Lawyer)
|
||||||
|
.ThenInclude(l => l.User)
|
||||||
|
.FirstOrDefault(meeting => meeting.TransactionID == _Transaction.TransactionID);
|
||||||
|
|
||||||
|
if (ShouldCreateRoom())
|
||||||
|
{
|
||||||
|
_LawyerVideoConferenceSchedule ??= new LawyerVideoConferenceSchedule
|
||||||
|
{
|
||||||
|
LawyerVideoConferenceSchedule_UID = Guid.CreateVersion7(),
|
||||||
|
CreatedOn = DateTime.UtcNow,
|
||||||
|
LawyerID = _Transaction.LawyerID.GetValueOrDefault(),
|
||||||
|
TransactionID = _Transaction.TransactionID,
|
||||||
|
};
|
||||||
|
_LawyerVideoConferenceSchedule.MeetingDate = DateTime.UtcNow;
|
||||||
|
_LawyerVideoConferenceSchedule.Status = nameof(VideoConferenceStatus.New);
|
||||||
|
|
||||||
|
var roomParticipants = new List<RoomParticipant>();
|
||||||
|
foreach (var participant in _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants)
|
||||||
|
{
|
||||||
|
var attendee = await CommunicationIdentityClient.CreateUserAsync();
|
||||||
|
roomParticipants.Add(new RoomParticipant(attendee) { Role = ParticipantRole.Attendee });
|
||||||
|
participant.MeetingRoomTokenID = await GetTokenResponse(attendee);
|
||||||
|
participant.MeetingRoomUserID = attendee.Value.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var presenter = await CommunicationIdentityClient.CreateUserAsync();
|
||||||
|
roomParticipants.Add(new RoomParticipant(presenter) { Role = ParticipantRole.Presenter });
|
||||||
|
_LawyerVideoConferenceSchedule.MeetingRoomTokenID = await GetTokenResponse(presenter);
|
||||||
|
_LawyerVideoConferenceSchedule.MeetingRoomUserID = presenter.Value.Id;
|
||||||
|
|
||||||
|
var roomsClient = new RoomsClient(CONNECTION_STRING);
|
||||||
|
CommunicationRoom room = await roomsClient.CreateRoomAsync(DateTime.Now, DateTime.Now.AddHours(2), roomParticipants);
|
||||||
|
|
||||||
|
_LawyerVideoConferenceSchedule.MeetingRoomID = room.Id;
|
||||||
|
|
||||||
|
if (_LawyerVideoConferenceSchedule.LawyerVideoConferenceScheduleID == 0)
|
||||||
|
{
|
||||||
|
_dbContext.Add(_LawyerVideoConferenceSchedule);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dbContext.Update(_LawyerVideoConferenceSchedule);
|
||||||
|
}
|
||||||
|
_dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentUser = _dbContext.Users.FirstOrDefault(u => u.User_UID == _currentUserService.GetUser_UID());
|
||||||
|
CommunicationUserToken = currentUser.Role == nameof(UserType.Notary)
|
||||||
|
? _LawyerVideoConferenceSchedule.MeetingRoomTokenID
|
||||||
|
: _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.First(u => u.ParticipantID == currentUser.UserID).MeetingRoomTokenID;
|
||||||
|
CommunicationRoomId = _LawyerVideoConferenceSchedule.MeetingRoomID;
|
||||||
|
CommunicationUserId = currentUser.Role == nameof(UserType.Notary)
|
||||||
|
? _LawyerVideoConferenceSchedule.MeetingRoomUserID
|
||||||
|
: _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.First(u => u.ParticipantID == currentUser.UserID).MeetingRoomUserID;
|
||||||
|
|
||||||
|
|
||||||
|
return Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetHashCode(string stringValue) => stringValue.GetHashCode().ToString("X8");
|
||||||
|
|
||||||
|
private async Task<string> GetTokenResponse(Response<CommunicationUserIdentifier> user)
|
||||||
|
{
|
||||||
|
var tokenResponse = await CommunicationIdentityClient.GetTokenAsync(user, new[] { CommunicationTokenScope.VoIP });
|
||||||
|
return tokenResponse.Value.Token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldCreateRoom() => _LawyerVideoConferenceSchedule == null || DateTime.UtcNow.Subtract(_LawyerVideoConferenceSchedule.MeetingDate).TotalHours > 1 || _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.Any(p => string.IsNullOrEmpty(p.MeetingRoomTokenID));
|
||||||
|
|
||||||
|
public string CommunicationRoomId { get; private set; }
|
||||||
|
public string CommunicationUserToken { get; private set; }
|
||||||
|
public string CommunicationUserId { get; set; }
|
||||||
|
|
||||||
|
public List<RoomParticipantViewModel> Participants
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var signatoryTypes = _Transaction.TransactionSignatories.Where(t => t.UserID > 0).ToDictionary(k => k.UserID, v => v.Type);
|
||||||
|
var dic = _LawyerVideoConferenceSchedule.LawyerVideoConferenceParticipants.ConvertAll(p => new RoomParticipantViewModel
|
||||||
|
{
|
||||||
|
Id = p.LawyerVideoConferenceParticipant_UID.ToString(),
|
||||||
|
DisplayName = $"{p.Participant.Firstname} {p.Participant.Lastname}",
|
||||||
|
RoomUserID = p.MeetingRoomUserID,
|
||||||
|
Type = signatoryTypes.GetValueOrDefault(p.ParticipantID, nameof(UserType.Principal))
|
||||||
|
});
|
||||||
|
|
||||||
|
var host = _LawyerVideoConferenceSchedule.Lawyer.User;
|
||||||
|
dic.Add(new RoomParticipantViewModel
|
||||||
|
{
|
||||||
|
DisplayName = $"{host.Firstname} {host.Lastname}",
|
||||||
|
Id = Guid.Empty.ToString(),
|
||||||
|
RoomUserID = _LawyerVideoConferenceSchedule.MeetingRoomUserID,
|
||||||
|
Type = nameof(UserType.Notary)
|
||||||
|
});
|
||||||
|
|
||||||
|
return dic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BindProperty(SupportsGet = true)]
|
||||||
|
public Guid Transaction_UID { get; set; }
|
||||||
|
|
||||||
|
private CommunicationIdentityClient CommunicationIdentityClient => _communicationIdentityClient ??= new CommunicationIdentityClient(CONNECTION_STRING);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
namespace EnotaryoPH.Web.Pages.Participant.VideoCall
|
||||||
|
{
|
||||||
|
public class RoomParticipantViewModel
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string RoomUserID { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
@if (User.Identity?.IsAuthenticated ?? false)
|
@if (User.Identity?.IsAuthenticated ?? false)
|
||||||
{
|
{
|
||||||
<a class="btn btn-primary ms-md-2" role="button" href="/Login?handler=Logout">Logout</a>
|
<a class="btn btn-primary ms-md-2" role="button" href="/Login?handler=Logout" data-bs-toggle="tooltip" data-bss-tooltip data-bs-placement="bottom" title="@User.Identity.Name">Logout</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"http": {
|
"http": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"applicationUrl": "http://localhost:5199",
|
"applicationUrl": "http://0.0.0.0:5199",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"https": {
|
"https": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"applicationUrl": "https://localhost:7121;http://localhost:5199",
|
"applicationUrl": "https://0.0.0.0:7121;http://0.0.0.0:5199",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,33 +0,0 @@
|
|||||||
const gulp = require("gulp"),
|
|
||||||
concat = require('gulp-concat'),
|
|
||||||
del = require('del'),
|
|
||||||
sass = require('gulp-sass')(require('sass')),
|
|
||||||
rename = require("gulp-rename"),
|
|
||||||
uglify = require('gulp-uglify');
|
|
||||||
|
|
||||||
const bundles = {
|
|
||||||
jfa: {
|
|
||||||
outputFileName: "wwwroot/dist/js/jfa.js",
|
|
||||||
inputFiles: [
|
|
||||||
"Assets/js/_Jfa.js",
|
|
||||||
"Assets/js/Page/_Page.js",
|
|
||||||
"Assets/js/Utilities/Routing/_Routing.js",
|
|
||||||
"Assets/js/Utilities/Element/_Element.js",
|
|
||||||
"Assets/js/Utilities/Request/_Request.js",
|
|
||||||
"Assets/js/Components/Dialog/_Dialog.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
gulp.task('bundle:jfa', function () {
|
|
||||||
return gulp.src(bundles.jfa.inputFiles, { base: "." })
|
|
||||||
.pipe(concat(bundles.jfa.outputFileName))
|
|
||||||
.pipe(gulp.dest("."))
|
|
||||||
.pipe(uglify())
|
|
||||||
.pipe(rename({ suffix: '.min' }))
|
|
||||||
.pipe(gulp.dest("."));
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('default', gulp.series(
|
|
||||||
gulp.parallel(['bundle:jfa'])
|
|
||||||
));
|
|
4943
EnotaryoPH/EnotaryoPH.Web/package-lock.json
generated
4943
EnotaryoPH/EnotaryoPH.Web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "jfa home",
|
"name": "eNotaryo",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "JFA Websites",
|
"description": "Electronic Notarization Platform",
|
||||||
"main": "index.js",
|
|
||||||
"author": "Jose Aquino Jr",
|
"author": "Jose Aquino Jr",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"browser": "wwwroot/dist/jfa.js",
|
||||||
|
"targets": {
|
||||||
|
"default": {
|
||||||
|
"context": "browser",
|
||||||
|
"outputFormat": "global",
|
||||||
|
"scopeHoist": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": "> 0.5%",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf wwwroot/dist",
|
||||||
|
"build:jfa": "npm run clean && parcel build Assets/js/_Jfa.js --dist-dir wwwroot/dist/js --no-source-maps --no-optimize && parcel build Assets/js/_Jfa.js --dist-dir wwwroot/dist/js2 --no-source-maps",
|
||||||
|
"build": "npm run build:jfa",
|
||||||
|
"start": "parcel watch Assets/js/_Jfa.js --dist-dir wwwroot/dist/js --no-source-maps"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"del": "6.1.1",
|
"parcel": "^2.13.3",
|
||||||
"gulp": "^5.0.0",
|
"rimraf": "^6.0.1"
|
||||||
"gulp-concat": "^2.6.1",
|
},
|
||||||
"gulp-rename": "^2.0.0",
|
"dependencies": {
|
||||||
"gulp-sass": "^5.1.0",
|
"@azure/communication-calling": "^1.33.2",
|
||||||
"gulp-uglify": "^3.0.2",
|
"@azure/communication-common": "^2.3.1"
|
||||||
"merge-stream": "^2.0.0",
|
|
||||||
"sass": "^1.54.9"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -7,6 +7,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnotaryoPH.Web", "EnotaryoP
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnotaryoPH.Data", "EnotaryoPH.Data\EnotaryoPH.Data.csproj", "{7B06785D-1B94-472B-885C-3C2F8834438B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnotaryoPH.Data", "EnotaryoPH.Data\EnotaryoPH.Data.csproj", "{7B06785D-1B94-472B-885C-3C2F8834438B}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D9FA3452-4AC7-4628-8531-4C6BC344427B}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\.gitignore = ..\.gitignore
|
||||||
|
..\azure-pipelines.yml = ..\azure-pipelines.yml
|
||||||
|
..\README.md = ..\README.md
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Loading…
x
Reference in New Issue
Block a user