Create a 'PWA minimal requirement' SW with workbox in a SPA - progressive-web-apps

I've a React SPA built with Vite ad I'm trying to make a basic PWA of it. I'm using the 'vite-pwa' plugin that auto inject the manifest and can generate the SW using google workbox behind the scenes.
What I want to achieve is to generate a very basic SW that if offline will respond with a custom fallback html page.
Since 'vite-pwa' relies on the generateSW method of workbox, I tried to setup it with the following
workbox: {
globPatterns: ['**/*.{png,svg,ico,txt,js,html,css}'],
navigationPreload: true,
runtimeCaching: [
{
handler: 'NetworkOnly',
urlPattern: ({request}) => request.mode == 'navigate',
options: {
precacheFallback: {
fallbackURL: '/offline.html'
}
}
}
],
}
The SW gets generated, installs correctly and precache what's needed.
Sadly when I go offline and try to access the page, it seems to respond with the default index.html which starts the SPA.
Next I tried the Offline Page only recipe that, however, does not use generateSW.
//sw.ts content
import { NetworkOnly } from 'workbox-strategies';
import { clientsClaim } from 'workbox-core'
declare let self: ServiceWorkerGlobalScope
cleanupOutdatedCaches()
precacheAndRoute(self.__WB_MANIFEST)
self.skipWaiting()
clientsClaim()
setDefaultHandler(new NetworkOnly());
offlineFallback();
The result is the same, when I go offline it responds with the default index.html.

Related

How to get data from an API only once (on app creation, outside component or view) in Vue3 SPA, with Pinia store

Is it possible and is it a good practice to avoid fetching data from an API every time the router view is loaded or the component is Mounted?
The thing is that some data rarely changes (like a dropdown options list, imagine allowed animal picks for my app) and it's logical not to send a request every time to a server, instead on app creation would be sufficient.
Tried in App.vue, is that a common thing?
IN APP.vue
import { computed, onMounted, onUpdated, ref } from 'vue';
onMounted(()=>{
axios.get('/data')....
.then((res)=>{
store.property = res.data
...
})
})
I think having it on mount in the App.vue component is acceptable since the App component would not be remounted.
The ideal setup, however, depends on some other parameters like size of application and size of team that's maintaining it. In a large applications you might want to organize things in amore structured and consistent way so you and other folks working on the code know where to find things.
You could consider moving the API call into the pinia action.
store.loadMyData()
// instead of
axios.get('/data')
.then((res)=>{
store.property = res.data;
})
That way you have fewer lines of code in the component. Having "lean" components and moving "business logic" out of components usually makes for better organization which makes it easier to maintain.
Within the action, you can track the state of the API
const STATES = {
INIT: 0,
DONE: 1,
WIP: 2,
ERROR: 3
}
export const useMyApiStore = defineStore('myapi', {
state: () => ({
faves: [],
favesState: STATES.INIT
}),
actions: {
loadMyData() {
this.store.favesState = STATES.WIP;
axios.get('/data')
.then((res) => {
this.store.property = res.data;
this.store.favesState = STATES.DONE;
})
.catch((e) => {
this.store.favesState = STATES.ERROR;
})
},
},
getters: {
isLoaded(){
return this.store.favesState === STATES.DONE;
}
isLoading(){
return this.store.favesState === STATES.WIP;
}
}
})
This is, obviously, more verbose, but allows for the components to be smaller and contain less logic. Then, for example, in your component you can use the getter isLoading to display a loading indicator, or use isLoaded inside another component to determine whether to show it.
Yes, this is a oft used way to load some data into the Vue App.
You could also load data before the Mounting in beforeMount() or created() Lifecycle Hooks (see Vue Lifecycle Diagram) to prevent unnecessary HTML updates.

Embedding a tableau dashboard into react messes up the formatting of the dashboard

I have a tableau dashboard that I am trying to embed into a react website using the tableau-api npm package. Although it looks fine on tableau public, the layout changes when I embed it. How do I ensure that the layout stays the same when I embed it in react?
Edit: here is my code. I used this answer as reference
import React, { Component } from 'react';
import tableau from 'tableau-api';
class Visualization extends Component {
componentDidMount() {
this.initTableau()
}
initTableau() {
const vizUrl = 'url';
const vizContainer = this.vizContainer;
let viz = new window.tableau.Viz(vizContainer, vizUrl)
}
render() {
return (
<div ref={(div) => { this.vizContainer = div }}>
</div>
)
}
}
export default Visualization;
There are a couple of options you should take a look at
Change the size of your dashboard from automatic to fixed (reference here). This will help if you have floating elements in your dashboard
Add your preferred device to the dashboard panel
Change the fit option to entire view
If none of these options work, I would recommend you share a photo of your dashboard and your code to be able to debug

How to change the shell title in Fiori App

I am looking at changing the ShellAppTitle in a Fiori app. Refer the highlighted part in the snapshot below:
I already know a way to to this which I am not proud of:
sap.ui.getCore().byId("shellAppTitle").getText() /.setText()
Is there any better approach to achieve this?
The only improvement I could think of is to implement this via the FLP ShellUIService rather than the getCore() method. The reason being that if SAP changes the id of the header text, your code will break since it is not designed to work this way.
To implement the service, first declare it in your manifest.json:
{
...
"sap.ui5": {
"services" : {
"ShellUIService": {
"factoryName": "sap.ushell.ui5service.ShellUIService"
}
}
}
...
}
Then, you can access it in your Component.js via the following code:
// Component.js (the app root component)
...
this.getService("ShellUIService").then( // promise is returned
function (oService) {
oService.setTitle("Application Title"); // also could use .getTitle() first
},
function (oError) {
jQuery.sap.log.error("Cannot get ShellUIService", oError, "my.app.Component");
}
);
...
The full documentation can be found in the SAPUI5 SDK

Non functioning default policy in SailsJS

I'm trying to implement the basic Passport integration in SailsJS. In my policies.js file, I have the default settings that every tutorial mentions.
'*': ['passport', 'sessionAuth'],
'auth': {
'*': ['passport']
}
My issue is that going to the main page localhost:1337/ doesn't seem to get passed through either policy. If I just set false there, everything still works. If I set false on the auth object for '*' though, I will get Forbidden on any /auth/* route. So, the policies seem to work, I just don't understand why the default catch-all doesn't. Thanks.
Do you use a controller or do you directly serve a view like in the sample homepage?
If you are serving the view directly with something similar to this:
// in config/routes.js
module.exports.routes = {
'/': {
view: 'homepage'
}
}
then you will have to modify it and use a controller in order to te able to use policies.
Create a route to a controller instead of a view:
// in config/routes.js
module.exports.routes = {
// Delete the previous definition and declare a route
// to a controller "index"
'get /': 'indexController.home'
}
Create the controller:
// in api/controllers/IndexController.js
module.exports = {
home: function (req, res) {
// Render the view located in "views/homepage.ejs"
res.view('homepage');
}
};
Then you will be able to manage the policies to apply to the controller index in the file config/policies.js.

Facebook action script 3 API login/logout issue

I'm making mobile AIR app for Android using Flash builder 4.5, AIR 2.6, Facebook action script 3 API the latest version.
I have a problem with login/logout. I can login only one time - then my data caches somehow and Facebook automatically logs me in. When I call logout I receive response TRUE, but I don't really logout from system. Standard login dialog doesn't appear for me. I have already read a lot of articles on stackoverflow and open issues on official site, but none of them were helpfull. How can I solve this? Here is the code I use:
package
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.external.ExternalInterface;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.system.Capabilities;
import flash.system.Security;
import flash.display.Loader;
import com.facebook.graph.FacebookMobile;
public class TestProj extends Sprite
{
public function TestProj()
{
super();
//register to add to stage
this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
}
private function onAddedToStage(event:Event):void
{
this.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
FacebookMobile.init("195053007196177", initCallback);
}
private function initCallback(success:Object, fail:Object):void
{
var appPermissions:Array = new Array("read_stream", "offline_access", "publish_stream", "read_friendlists");
FacebookMobile.login(loginCallback, this.stage, appPermissions);
//FacebookMobile.logout(logoutCallback);
}
private function loginCallback(success:Object, fail:Object):void
{
//And here I always receive success with my UserID
//and login dialog don't appears to me before this
if (success)
{
trace("login ok");
}
else
trace("login failed");
}
private function logoutCallback(success:Object):void
{
//here I reseive "TRUE" always!!
trace(success);
}
}
}
You're only passing the 1st argument of logoutCallback to your logout method. If you add in the 2nd argument of your site url specified for your app, it should clear it out the html cookie for that window. Also, set FacebookMobile.manageSession = false;
FacebookMobile.logout(logoutCallback, "http://your_app_origin_url");
There is a potential, related bug that involves Desktop and Mobile not accessing or clearing the access token's the same way. For that, there's a hack that describes exposing the access token in FacebookMobile, then manually calling the "logout" method with the access token. The issue is described here, including a method called "reallyLogout". If what I've written above doesn't work, implement "reallyLogout".
When you log out, your app clears the local session but does not log you out of the system. This is clearly defined in the documentation for logout. Think about it, if you're logged into Facebook on your Smartphone, Web Browser, and now this Mobile Desktop App, and suddenly you log out... it shouldn't log you out EVERYWHERE, just within that browsers session. So pass that 2nd parameter.
I've had this exact problem, and after trying numerous fixes, this finally seems to work:
The default logout functionality seems to not be properly clearing cookies via the FacebookMobile actionscript API. The solution in comment #33 here worked for me, reproduced here. Make sure to sub in your own APP_ID:
function logout(e:MouseEvent):void {
FacebookMobile.logout(onLogout, "https://m.facebook.com/dialog/permissions.request?app_id=APP_ID&display=touch&next=http%3A%2F%2Fwww.facebook.com%2Fconnect%2Flogin_success.html&type=user_agent&perms=publish_stream&fbconnect=1");
}
function onLogout(result:Object):void
{
trace("Perfect Log Out!")
}
Have had this Android Facebook clean logout problem the whole day, manage to solve it. Hope it helps. Here is my FB mobile handlelogin code to ensure all fb cookies and sessions are being removed and user will need to relogin.
Sometimes FB server is very slow. Its best to put a timer before you call handleLoginClick() again
function handleLoginClick():void
{
trace("connecting to facebook");
if (FacebookMobile.getSession() == null)
{
FacebookMobile.init(APP_ID, onHandleInit, null);
FacebookMobile.manageSession = false
}
else
{
var webView:StageWebView = new StageWebView();
webView.viewPort = new Rectangle(0, 0, 1, 1);
webView.stage = this.stage;
webView.loadURL("https://m.facebook.com/logout.php?confirm=1&next=http://www.facebook.com&access_token=" + FacebookMobile.getSession().accessToken);
webView.addEventListener(Event.COMPLETE,webviewhandleLoad);
function webviewhandleLoad(e:Event)
{
FacebookMobile.logout(null, "http://apps.facebook.com/<appName>/");
FacebookMobile.logout(null, "http://www.facebook.com");
webView.dispose()
webView = null
setTimeout(handleLoginClick,3000)
}
}
}
look at the solution of this problem. Maby someone it helps:
var stage_ref:Stage = PlatformUtil.originalStage(); //my custom class to get stage
var webView:StageWebView = new StageWebView();
webView.viewPort = new Rectangle(0, 0, stage_ref.width, stage_ref.height);
FacebookMobile.login(loginCallback, stage_ref, appPermissions, webView);
http://code.google.com/p/facebook-actionscript-api/issues/detail?id=381
http://code.google.com/p/facebook-actionscript-api/issues/detail?id=382
http://code.google.com/p/facebook-actionscript-api/issues/detail?id=383