I am trying to implement the Web Share API for some text I want to allow users to copy/share, and it's been successful except for an issue with Safari desktop. I check for navigator.share and if it exists, then only do I open the native share screen, and if it doesn't, I just copy the text straight to clipboard (like on desktop).
Safari desktop DOES support the Web Share API, however it doesn't seem to provide a way to just copy it? You can see in the screenshot it just gives some options for me. Am I missing something? Is there no way to have "Copy" as an option?
const copyURL = copyText => {
if (navigator.share) {
navigator
.share({ text: copyText })
.then(() => {})
.catch(console.error);
} else {
navigator.permissions.query({ name: 'clipboard-write' }).then(result => {
if (result.state === 'granted' || result.state === 'prompt') {
navigator.clipboard.writeText(copyText).then(() => {
setLinkCopied(true);
});
}
});
}
};
There is indeed no "Copy" feature in desktop Safari's share sheet. The good news is that Safari supports the Async Clipboard API, so you can easily use it as an alternative, as shown in the example below:
async function copyPageUrl() {
try {
await navigator.clipboard.writeText(location.href);
console.log('Page URL copied to clipboard');
} catch (err) {
console.error('Failed to copy: ', err);
}
}
Related
I have a button with an address, and when it opens, I want to use the "default" app which is installed. The reason is, for example, many iOS users uninstall Apple Maps app, so they only have Google. Checking if iOS ? 'maps' : 'google' isn't safe because it can't be Platform dependent.
This is using Expo SDK 46.
I then read to try something like:
const openUrl = () => {
const mapNames = ['comgooglemaps', 'maps'];
const hasApp = mapNames.find(async name => {
try {
return await Linking.canOpenURL(
`${name}://?center=${vehicle.coordinates.latitude}, ${vehicle.coordinates.longitude}`,
);
} catch (_e) {
return false;
}
});
openMap({
provider: hasApp,
end: vehicle.streetAddress,
});
};
but this isn't working because Linking.canOpenURL is always returning the first item since it's a "string" and there fore meets the API requirements of "given URL can be handled".
So I tried an alternate option, based on research on other suggestions:
const openUrl = async () => {
let hasGoogleMaps = false;
await Linking.canOpenURL('comgooglemaps').then(canOpen => {
if (canOpen) {
hasGoogleMaps = true;
}
});
openMap({
provider: hasGoogleMaps ? 'google' : 'apple',
end: vehicle.streetAddress,
});
};
This too fails to open Google Maps on iOS.
My question is: how can I for sure know if I have google maps installed and not base it on the Platform.OS itself?
Bonus question: is it true I cannot install Google Maps on a simulator?
I have an Angular web app that opens a OneDrive FilePicker and uses a Google Firebase cloud function to transfer the files the user picks to Google storage. It works fine if I use a personal OneDrive account, but if it is a business account I get a CORS error and the files are not transferred. My client-side code looks like this:
oneDriveOptions = {
clientId: '#####',
action: 'download',
multiSelect: true,
advanced: {
redirectUri: `${Utils.getBaseURL()}/one-drive-redirect`,
},
success: (files: any[]) => {
this.zone.run(() => {
this.oneDriveCallback(files);
});
},
cancel: () => {
this.zone.run(() => {
this.dbService.indeterminateProgress = false;
});
},
error: () => {
this.zone.run((error) => {
this.dbService.indeterminateProgress = false;
console.error('OneDrive file picker error:', error);
});
},
};
async oneDriveClicked() {
OneDrive.open(this.oneDriveOptions);
this.dialogRef.close();
}
async oneDriveCallback(files: any) {
try {
this.dbService.indeterminateProgress = true;
const cloudServiceFiles = this.convertOneDriveFiles(files.value);
await this.dbService.addFromCloudService(this.data.dbData, files.accessToken, cloudServiceFiles, this.data.insertIdx, 'oneDrive');
this.dbService.indeterminateProgress = false;
} catch (error) {
console.error(error.message);
this.dbService.indeterminateProgress = false;
}
}
And on the client side:
const getOneDriveFile = async (cloudServiceFile: CloudServiceFile) => {
const fileStream = oneDriveApi.items.download({
accessToken: cloudServiceAuthToken,
itemId: cloudServiceFile.id,
// drive:, default: 'me'. If it's set to be either 'user'/'drive'/'group'/'site', driveId has to be set to.
// driveId: The id of the drive that was shared to us. Must be set if params.drive is set.
});
return fileStream;
};
The output I get in the console:
Any suggestion why this code works fine for a personal account but gives CORS error from a business account?
This means you didn't register your app in Microsoft system so that they didn't let you to use the one drive .
follow this steps then check your app again
Setting up
To get started you need to register your application and receive an app ID from the Azure App registrations page.
To get started you need to register your application and receive an app ID from the Azure App registrations page.
Log in to the Azure App registrations page using your Microsoft account, or a work or school account.
Click Add an app and enter a name for your app.
After your application is created, configure it to support the JavaScript picker:
Click Generate New Password to create an Application secret. While this value is not necessary for the picker, it must have been created.
Click Add Platform and then select Web.
Enter one or more URLs where the picker will be hosted on your website. Each page that hosts the picker needs to have a redirect URL provided.
Click the Save button to save your changes.
Copy the Application Id for your application and use it in the JavaScript options object you provided to open or save a file.
I have a Web app built in Vuejs and has SSO authentification using microsoftTeams.authentication.getAuthToken when running in Teams, or microsoftAuthLib when running in the browser.
Inside the company's network or when connected to the VPN everything works absolutely fine.
We recently opened it outside of the VPN and we created a public certificate for it. So when I disconnect the VPN, it works:
In any browser (outside of Teams).
Teams browser version.
Teams on Android/iPhone.
But it doesn't work on Teams Windows Desktop version, it fails with the following error:
Refused to display
'https://login.microsoftonline.com/.../oauth2/authorize?...' in a
frame because it set 'X-Frame-Options' to 'deny'.
Anybody has an idea what could be the issue? And why would it work on the company's VPN but not outside?And only on specific cases? I am lost, any help would be appreciated.
Thank you
*** EDIT / ADDED SSO REDIRECT CODE ***
import * as microsoftTeams from "#microsoft/teams-js";
import * as microsoftAuthLib from "msal";
import settings from './settings.js';
var msalConfig = {
auth: {
clientId: settings.sso.id,
authority: settings.sso.authority
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var requestObj = {
scopes: settings.sso.scopes
};
var myMSALObj = new microsoftAuthLib.UserAgentApplication(msalConfig);
myMSALObj.handleRedirectCallback(authRedirectCallBack);
function authRedirectCallBack(error, response) {
if (error) {
console.log(error);
} else {
console.log("token type is:" + response.tokenType);
}
}
function loginRedirect(requestObj) {
let account = myMSALObj.getAccount();
if (!account) {
myMSALObj.loginRedirect(requestObj);
return false;
} else {
return true;
}
}
function acquireMsalToken() {
return new Promise(function (resolve) {
resolve(myMSALObj.acquireTokenSilent(requestObj).then(token => {
return token.accessToken;
}).catch(error => {
acquireMsalTokenRedirect(error);
}));
})
}
function acquireTeamsToken() {
return new Promise((resolve, reject) => {
microsoftTeams.authentication.getAuthToken({
successCallback: (result) => {
resolve(result);
},
failureCallback: (error) => {
reject(error);
}
});
});
}
function acquireMsalTokenRedirect(error) {
if (error.errorCode === "consent_required" ||
error.errorCode === "interaction_required" ||
error.errorCode === "login_required") {
myMSALObj.acquireTokenRedirect(requestObj);
}
}
var msal = {
autoSignIn: function () {
return loginRedirect(requestObj);
},
acquireToken: async function () {
if (settings.sso.inTeams) {
microsoftTeams.initialize();
microsoftTeams.enterFullscreen();
return acquireTeamsToken();
} else {
let signedIn = msal.autoSignIn();
if (signedIn) {
return acquireMsalToken();
}
}
}
}
export default msal
This error means that you are trying to redirect your tab's iframe to the AAD login flow which in turn is unable to silently generate an auth token for you and is attempting to show an interactive flow (e.g. sign in or consent):
Refused to display
'https://login.microsoftonline.com/.../oauth2/authorize?...' in a
frame because it set 'X-Frame-Options' to 'deny'.
To avoid this issue you need to try and acquire a token silently and if that fails use the microsoftTeams.authentication.authenticate API to open a popup window and conduct the AAD login flow there.
Replacing the acquireTeamsToken() function with the following resolved the issue.
function acquireTeamsToken() {
return new Promise((resolve, reject) => {
microsoftTeams.initialize(() => {
microsoftTeams.authentication.authenticate({
url: window.location.origin + "/ms-teams/auth-start",
width: 600,
height: 535,
successCallback: (result) => {
resolve(result);
},
failureCallback: (error) => {
reject(error);
}
});
});
});
}
I found this documentation very helpful on how to create the Authentication pop up and how to create a Callback window with the Token in it.
You might also want to cache the token and only create a popup when it expires.
This might be because you're using the auth popup option instead of the redirect option in whichever auth library you're using (hopefully MSAL 2.0). Teams is a little different because it's actually launching a popup for you when necessary, so although it sounds a bit strange, you actually want to use the redirect option, inside the popup that is launched. What might help is to look at the new SSO Sample app in the Teams PnP samples.
Go to: %APPDATA%\Microsoft\Teams
Open the file hooks.json (if it's not there, create it)
Add the following to it: {"enableSso": false, "enableSsoMac": false}
That's it, now Teams desktop has the same authentication workflow as the browser version. Have a nice day.
i hope y'all will be fine,
I am a beginner in webRTC sorry if my question feels like a noob one but i was wondering that is there any proper way to close the connection among peers especially using simple-peer.js, looking forward for your awesome replies
here is my sample react code,
if (navigator.getUserMedia) {
navigator.getUserMedia({ audio: { echoCancellation: true }, video: true }, stream => {
let peer = new Peer({
initiator: this.props.isInitiator,
stream
});
this.localStream.current.srcObject = stream;
this.localStream.current.play();
peer.on('signal', (data) => {
socket.emit('offer', { data: JSON.stringify(data), conversation_id: this.props.conversation_id })
});
socket.on('offer', (data) => {
peer.signal(JSON.parse(data))
})
socket.on('DESTROY-VIDEO-CALL-SESSION', () => {
stream.getTracks().forEach(track => track.stop());
peer.removeAllListeners();
peer.destroy();
});
peer.on('stream', (streamData) => {
this.remoteStream.current.srcObject = streamData;
this.remoteStream.current.play();
});
this.setState({
endCall: () => {
socket.emit('VIDEO-CALL-ENDED', this.props.conversation_id);
}
})
}, error => {
alert('Please allow video and audio permission to make this call')
});
}
The function is peer.destroy().
Fyi, if you need to find undocumented API functions in Javascript libraries, you can do this by running your JS file in the browser and pressing F12 to bring up the debugging console. Set a breakpoint after the object that you want to investigate is instantiated:
I set a breakpoint AFTER I instantiated peer1 (line 51). Then I hovered over peer1 (line 50) to pop up a menu of all of the properties (methods and fields) belonging to this object. If you're looking for a particular function as in this case, sometimes you find it immediately in this menu, but often you have to expand the property called __proto__ :
You can see the destroy function located here. It's just a guess whether or not a function found in this manner actually does what you want it to do, but luckily in this case it does what we want.
Is it possible to preload / pre-populate a database in my React Native application and then the first time it is run, simply do a sync? I already have most, if not all of the database information before the app is distributed, it would be awesome if it just had to do a quick sync when the app is run. Any ideas how I would go about doing that?
I found this - https://pouchdb.com/2016/04/28/prebuilt-databases-with-pouchdb.html but it doesn't mention React Native
Using:
pouchdb-find: ^7.0.0
pouchdb-react-native: ^6.4.1
react: 16.3.1
react-moment: ^0.7.9
react-native: ~0.55.2
Thanks for any pointers.
Update Here is the code I'm using to try the loading of a dump file. This code exists in /screens/Home.js
The dump file is located in /client/dbfile/database.txt
var db = new PouchDB("cs1");
db.get("_local/initial_load_complete")
.catch(function(err) {
console.log("loading dumpfile");
if (err.status !== 404) {
// 404 means not found
throw err;
}
db.load("/client/dbfile/database.txt").then(function() {
return db.put({ _id: "_local/initial_load_complete" });
});
})
.then(function() {
// at this point, we are sure that
// initial replication is complete
console.log("loading is complete!");
return db.allDocs({ include_docs: true });
})
.then(
function(res) {
// display all docs for debugging purposes (empty)
console.log(res);
});
this.localDB = db;
When this runs my console displays this - showing there have been 0 rows added.
Object {
"offset": 0,
"rows": Array [],
"total_rows": 0,
}
Possible Unhandled Promise Rejection (id: 0):
Object {
"message": undefined,
"name": "unknown",
"status": 0,
}
In my project I have couple of db docs I distribute with app (translations JSON is the one good example).
So at app init I just try to read translations doc from db, if there is none - I import content from js module and store in db.
Then translations changes just being replicated from server to local db.
//transmob.js
const transMobFile = {
//content goes here
);
module.exports = transMobFile
//dbInit.js
import transMobFile from 'data/transMob';
..
PDB.getDoc('TransMob')
.then((doc)=> {
if (!doc) {
global.locales = transMobFile.localesMob; // locales
global.translations = transMobFile.langMob; // translations
return PDB.saveDoc('TransMob', transMobFile)
}
})
You can use react-native-fs to load a file from /android/app/src/main/assets. Just put the file into the assets folder and read it with RNFS.readFileAssets.
import PouchDB from 'pouchdb-core';
PouchDB
.plugin(require('pouchdb-find'))
.plugin(require('pouchdb-load'));
import RNFS from 'react-native-fs';
const localDB = new PouchDB("cs1", {adapter: 'asyncstorage'});
localDB.get("_local/initial_load_complete")
.catch(function(err) {
console.log("loading dumpfile");
if (err.status !== 404) {
// 404 means not found
throw err;
}
RNFS.readFileAssets('yourdb.txt', 'utf8')
.then((contents) => {
localDB.load(contents).then(function() {
return localDB.put({ _id: "_local/initial_load_complete" });
}).then(function() {
// at this point, we are sure that
// initial replication is complete
console.log("loading is complete!");
return localDB.allDocs({ include_docs: true }).then(
function(res) {
// display all docs for debugging purposes (empty)
console.log(res);
});
}, function(err) {
console.log(err);
});
})
})
You'll need to rebuild your project, reloading is not sufficient.
My project crashes when I attempt to load a 30MB file, so I probably will split it into a few smaller files. Check out https://github.com/pouchdb-community/pouchdb-load to see how this works if needed.
I found that the db.load() function from the pouchdb-load module requires a URL. I was pointing it to a file path on the device's filesystem. I placed my database.txt file on my server, changed it to use the url and it worked.
In my mind this isn't ideal because if they install the app and have slow wireless, it still has to pull the file from the server. It is still much faster than performing a full-on replicate when the app opens for the first time however.