I am working on a Blazor application which needs the screen to be shared. I found how to read and display the screen in the Razor page, once it receives the signaled message from the client, which is an Electron application. This is the code for the Electron Application.
const { desktopCapturer } = require('electron')
const signalR = require('#microsoft/signalr')
let connection;
let subject;
let screenCastTimer;
let isStreaming = false;
const framepersecond = 10;
const screenWidth = 1280;
const screenHeight = 800;
async function initializeSignalR() {
connection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:51064")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on("NewViewer", function () {
if (isStreaming === false)
startStreamCast()
});
connection.on("NoViewer", function () {
if (isStreaming === true)
stopStreamCast()
});
await connection.start().then(function () {
console.log("connected");
});
return connection;
}
initializeSignalR();
function CaptureScreen() {
return new Promise(function (resolve, reject) {
desktopCapturer.getSources({ type: ['screen'], thumbnailSize: { width: screenWidth, height: screenHeight } },
(error, sources) => {
if (error) console.error(error);
for (const source of sources) {
if (source.name === 'Entire screen') {
resolve(source.thumbnail.toDataURL())
}
}
})
})
}
const agentName = document.getElementById('agentName');
const startCastBtn = document.getElementById('startCast');
const stopCastBtn = document.getElementById('stopCast');
stopCastBtn.setAttribute("disabled", "disabled");
startCastBtn.onclick = function () {
startCastBtn.setAttribute("disabled", "disabled");
stopCastBtn.removeAttribute("disabled");
connection.send("AddScreenCastAgent", agentName.value);
};
function startStreamCast() {
isStreaming = true;
subject = new signalR.Subject();
connection.send("StreamCastData", subject, agentName.value);
screenCastTimer = setInterval(function () {
try {
CaptureScreen().then(function (data) {
subject.next(data);
});
} catch (e) {
console.log(e);
}
}, Math.round(1000 / framepersecond));
}
But I am trying to figure out the equivalent of these lines of code:
const { desktopCapturer } = require('electron')
const signalR = require('#microsoft/signalr')
desktopCapturer.getSources({ type: ['screen'], thumbnailSize: { width: screenWidth, height: screenHeight } },
in an HTML page when signalR.js is used.
Help would be very much appreciated !
Related
I have a collection in Strapi called projects and I want to be able to fetch only the projects belonging to the currently logged in user. I'm using Next.js with NextAuth on the frontend and I'm currently filtering the results using:
/api/projects?filters[user][id][$eq]=${session.id}
This works fine except the endpoint still allows a user to fetch projects for all users if accessed directly. I'm thinking a better approach would be to setup a custom API endpoint in Strapi which would be something like /api/projects/:user. Is this the best way to acheive this? I've managed to setup a custom endpoint in Strapi using the CLI but I'm not sure what logic needs to go in the controller. Would modifiying an exisiting endpoint be better?
Any advice appreciated, thanks!
Custom endpoint create is good idea. I had same problem. Once i created custom endpoint then i got data with entitiyservice. It's work. Below image is my code.
./scr/api/[collection]/controllers/[collection].js
'use strict';
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::user-profile.user-profile', ({ strapi }) => ({
async me(ctx) {
try {
const user = ctx.state.user;
const datas = await strapi.entityService.findMany("api::user-profile.user-profile", {
filters: {
user: {
id: user.id
}
}
})
return datas;
} catch (err) {
ctx.body = err;
}
}
}));
If you will use all endpoints in collection like (create,update,delete,find,findone). You must override the all endpoints . Example is the below.
'use strict';
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::education.education', ({ strapi }) => ({
async create(ctx) {
try {
const user = ctx.state.user;
ctx.request.body.data.users_permissions_user = user.id
const datas = await strapi.entityService.create("api::education.education", {
...ctx.request.body
})
return datas;
} catch (err) {
ctx.body = err;
}
},
async update(ctx) {
try {
const user = ctx.state.user;
ctx.request.body.data.users_permissions_user = user.id
const { id } = ctx.params;
const experienceData = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
},
id: id
}
});
if (experienceData.length === 0) {
return {
data: null,
error: {
message: ''
}
}
}
const datas = await strapi.entityService.update("api::education.education", id, {
...ctx.request.body
})
return datas;
} catch (err) {
ctx.body = err;
}
},
async delete(ctx) {
try {
const user = ctx.state.user;
const { id } = ctx.params;
const experienceData = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
},
id: id
}
});
if (experienceData.length === 0) {
return {
data: null,
error: {
message: ''
}
}
}
const datas = await strapi.entityService.delete("api::education.education", id)
return datas;
} catch (err) {
ctx.body = err;
}
},
async findOne(ctx) {
try {
const user = ctx.state.user;
const { id } = ctx.params;
const experienceData = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
},
id: id
}
});
if (experienceData.length === 0) {
return {
data: null,
error: {
message: ''
}
}
}
const datas = await strapi.entityService.findOne("api::education.education", id)
return datas;
} catch (err) {
ctx.body = err;
}
},
async find(ctx) {
try {
const user = ctx.state.user;
const datas = await strapi.entityService.findMany("api::education.education", {
filters: {
users_permissions_user: {
id: user.id
}
}
})
return datas;
} catch (err) {
ctx.body = err;
}
},
}));
No extra endpoints and no extra codes.
Strapi v4
Yes, creating separate endpoint for this task would be great.
Instead of /api/projects/:user using this type of route, use /api/projects as you can get current logged in users details from ctx.state.user
No, Instead of modifying your existing controller create new controller and use that controller to satisfy your needs.
I ended up extending my controller. In src/api/controllers/project.js I made the following changes:
"use strict";
const { createCoreController } = require("#strapi/strapi").factories;
module.exports = createCoreController("api::project.project", {
async find(ctx) {
const user = ctx.state.user;
ctx.query.filters = {
...(ctx.query.filters || {}),
user: user.id,
};
return super.find(ctx);
},
});
Then simply call the /api/projects endpoint.
Answer based on this guide Limit access of Strapi users to their own entries.
I am trying to upload a file with vue, but the issue I am stuck with is this,
I can't access this.imageFile.value after selecting the photo.
The data I returned from setup but undefined.
Under normal conditions, imageFile.value works in setup.
<template>
<img v-show="imageUrl" :src="imageUrl" alt="">
<input #change="handleImageSelected,getdata" ref="fileInput" type="file" accept="image/*">
</template>
<script>
import {UseImageUpload} from "./use/UseImageUpload";
export default {
data() {
return {
Hello: null
}
},
methods: {
getdata() {
this.Hello = this.imageFile.value
}
},
setup() {
let {imageFile, imageUrl, handleImageSelected} = UseImageUpload();
return {
imageFile,
handleImageSelected,
imageUrl,
}
}
}
</script>
UseImageUpload.js
import {ref, watch} from "vue";
export function UseImageUpload() {
//image
let imageFile = ref("");
let imageUrl = ref("");
function handleImageSelected(event) {
if (event.target.files.length === 0) {
imageFile.value = "";
imageUrl.value = "";
return;
}
imageFile.value = event.target.files[0];
}
watch(imageFile, (imageFile) => {
if (!(imageFile instanceof File)) {
return;
}
let fileReader = new FileReader();
fileReader.readAsDataURL(imageFile);
fileReader.addEventListener("load", () => {
imageUrl.value = fileReader.result;
});
});
return {
imageFile,
imageUrl,
handleImageSelected,
};
}
First of all please try not to mix Options and Composition API - I know it might work but it is not necessary and in the most cases just an anti-pattern.
Composition API is there to replace the Options API or rather to give an alternative. They are just not supposed to work together or to be used together.
So this would improve your code:
setup() {
const Hello = ref(null);
const {imageFile, imageUrl, handleImageSelected} = UseImageUpload();
function getdata() {
Hello = imageFile.value
}
return {
imageFile,
handleImageSelected,
imageUrl,
getdata,
}
This should also fix your issue.
I've been working on getting a custom tab working in MS teams using MSAL. I've been following the example here: https://github.com/nmetulev/teams-msal and I can generate a token. I then try to forward this token to my API, where I build a security claim and call SignInAsync() to persist the cookie.
This then gets stored, and I forward to my standard page, but this does not page auth (I get unauthorized). Is there something I'm missing that I need to be doing?
Auth Page
const signIn = () => {
msalApp.acquireTokenRedirect(authenticationParameters);
}
const handleSignedIn = () => {
microsoftTeams.initialize();
microsoftTeams.authentication.notifySuccess();
}
const handleSignedOut = (error) => {
microsoftTeams.initialize();
microsoftTeams.authentication.notifyFailure(error);
}
const handleErrorReceived = (authError, accountState) => {
console.log(authError, accountState);
handleSignedOut({authError});
}
const handleTokenReceived = (response) => {
console.log(response);
handleSignedIn();
}
// MAIN
const msalApp = new Msal.UserAgentApplication(msalConfig);
msalApp.handleRedirectCallback((response) => handleTokenReceived(response), (error, state) => handleErrorReceived(error, state));
microsoftTeams.initialize();
microsoftTeams.getContext((context) => {
authenticationParameters = {
scopes: scopes,
loginHint: context.loginHint
};
setTimeout(() => {
attemptSilentSignIn().then(success => {
if (success) {
handleSignedIn();
} else {
signIn();
}
});
},
4000);
});
Sign In Page:
const attemptSilentSignIn = () => {
renderLoading();
if (msalApp.getAccount()) {
msalApp.acquireTokenSilent({ scopes }).then((response) => {
if (response && response.accessToken) {
handleSignedIn(response.accessToken);
} else {
handleSignedOut();
}
}, () => {
handleSignedOut();
})
} else {
handleSignedOut();
}
}
const signIn = () => {
renderLoading();
microsoftTeams.initialize(() => {
microsoftTeams.authentication.authenticate({
url: window.location.origin + "/resources/TeamsAuthFlow.html",
successCallback: () => attemptSilentSignIn(),
failureCallback: (error) => renderError(error)
});
});
}
const handleSignedIn = (accessToken) => {
microsoftTeams.initialize();
microsoftTeams.getContext((context) => {
var tenant = $("<input>").attr("id", "TenantId").attr("name", "TenantId").val(context.tid);
var token = $("<input>").attr("id", "AuthToken").attr("name", "AuthToken").val(accessToken);
var form = $("<form>").css("display", "none").attr("id", "target").attr("method", "POST").attr("action", "/api/TeamsTabSignIn").append(tenant).append(token).submit();
$("body").append(form);
$("#target").submit();
});
}
const handleSignedOut = () => {
renderSignedOutView();
}
// MAIN
let app = document.querySelector('.app');
const msalApp = new Msal.UserAgentApplication(msalConfig);
attemptSilentSignIn();
let authenticationParameters = null;
const handleErrorReceived = (authError, accountState) => {
console.log(authError, accountState);
handleSignedOut({ authError });
}
const handleTokenReceived = (response) => {
console.log(response);
handleSignedIn();
}
API Call
TenantId = Context.Request.Form["TenantId"];
AuthToken = Context.Request.Form["AuthToken"];
var principal = await _authHelper.SetPlatformUser(TenantId, AuthToken);
if (principal is ClaimsPrincipal cp)
{
await Context.SignInAsync("Cookies", cp, new AuthenticationProperties { IsPersistent = true });
Response.Redirect("/app/teamspage/Ticket");
}
Description :- ionic-angular project unit test for SQLite mock not working below is the mock class.
I'm wants to do the unit test for cordova SQLite, using jasmine framework
declare var SQL;
export class SQLiteObject {
_objectInstance: any;
constructor(_objectInstance: any) {
this._objectInstance = _objectInstance;
}
executeSql(statement: string, params: any): Promise<any> {
return new Promise((resolve, reject) => {
try {
console.log(statement);
const st = this._objectInstance.prepare(statement, params);
const rows: Array<any> = [] ;
while (st.step()) {
const row = st.getAsObject();
rows.push(row);
}
const payload = {
rows: {
item(i) {
return rows[i];
},
length: rows.length
},
rowsAffected: this._objectInstance.getRowsModified() || 0,
insertId: this._objectInstance.insertId || void 0
};
// save database after each sql query
const arr: ArrayBuffer = this._objectInstance.export();
localStorage.setItem('database', String(arr));
resolve(payload);
} catch (e) {
reject(e);
}
});
}
sqlBatch(statements: string[], params: any): Promise<any> {
return new Promise((resolve, reject) => {
try {
const rows: Array<any> = [];
for (const statement of statements) {
console.log(statement);
const st = this._objectInstance.prepare(statement, params);
while (st.step()) {
const row = st.getAsObject();
rows.push(row);
}
}
const payload = {
rows: {
item(i) {
return rows[i];
},
length: rows.length
},
rowsAffected: this._objectInstance.getRowsModified(),
insertId: this._objectInstance.insertId || void 0
};
// save database after each sql query
const arr: ArrayBuffer = this._objectInstance.export();
localStorage.setItem('database', String(arr));
resolve(payload);
} catch (e) {
reject(e);
}
});
}
}
export class SQLiteMock {
public create(config: SQLiteDatabaseConfig): Promise<SQLiteObject> {
let db;
const storeddb = localStorage.getItem('database');
if (storeddb) {
const arr = storeddb.split(',');
db = new SQL.Database(arr);
} else {
db = new SQL.Database();
}
return new Promise((resolve, reject) => {
resolve(new SQLiteObject(db));
});
}
}
Error :- Sql not defined
I just followed this article https://www.techiediaries.com/mocking-native-sqlite-plugin/
Environment :-
Ionic 5
Angular 9
Pls share any alternate approach & possibilities..
Thanks.
In fact, this is caused by your sql.js version being too high.
declare var SQL should be replaced with declare const initSqlJs: any;
The complete code of SqliteMock is then as follows:
import { SQLiteDatabaseConfig } from '#ionic-native/sqlite/ngx';
import { SQLiteObject } from './sqlite-object';
declare const initSqlJs: any;
const sqlConfig = {
locateFile: filename => `./assets/js/sql-wasm.wasm`
};
export class SqliteMock {
public create(config: SQLiteDatabaseConfig): Promise<SQLiteObject> {
return initSqlJs(sqlConfig).then((sql) => {
const db = new sql.Database();
return new Promise( (resolve, reject) => {
resolve(new SQLiteObject(db));
});
});
}
}
Code reference
And the index.html should then introduce sql-wasm.js
Also don't forget to put sql-wasm.js and sql-wasm.wasm under the assets/js/ folder
I don't know English so this is my answer through translator.
I want to upload image from my RN app to meteor backend. I am using "react-native-image-picker": "^0.26.7" for getting imagefile from gallery or camera and uploading to meteor using package react-native-meteor to collectionFs this is my code of RN app where I am calling meteor method for image upload as soon as user select image:
_handleSelectFile() {
const { order } = this.state;
var options = {
title: 'Select Avatar',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
ImagePicker.showImagePicker(options, (response) => {
if (response.didCancel) {
console.log('User cancelled image picker');
}
else if (response.error) {
console.log('ImagePicker Error: ', response.error);
}
else {
// let source = { uri: response.uri };
// You can also display the image using data:
let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
order: {
...order,
fileName: response.fileName
}
});
let fileData = response.data;
// const body = new FormData();
// body.append('file',fileData);
var photo = {
url: fileData,
type: 'image/jpeg',
name: 'photo.jpg',
};
Meteor.FSCollection('orderImages').insert(photo, function (err, res) {
if (err) {
console.log('error during uploading');
} else {
console.log('uploading successfully');
// _this.props.navigator.pop();
}
});
}
});
}
and this is my server side code:
export const Orders = new Mongo.Collection('orders');
export const OrderImages = new FS.Collection("orderImages", {
filter: {
maxSize: 1048576,
allow: {
contentTypes: ['image/*'],
}
},
stores: [new FS.Store.FileSystem("orderImages")]
});
if (Meteor.isServer) {
OrderImages.allow({
insert: function () {
return true;
}
});
}
and I am getting error like this:
ExceptionsManager.js:65
Cannot read property 'apply' of undefined