os-js / osjs-client

OS.js Client Module
https://manual.os-js.org/
Other
31 stars 31 forks source link

How to disable osjs client? #209

Closed maryam4s26 closed 9 months ago

maryam4s26 commented 9 months ago

hi @andersevenrud . Is it possible to disable the client when necessary so that the user cannot do anything or prevent user interactions and display to the user an error dialog?

andersevenrud commented 9 months ago

I'm not really sure what you mean by "disabling" the client exactly.

But if you just want to block input, then you can create a service provider that places a transparent element into the DOM at the max z-index that captures the events instead of the desktop/panels/windows/etc.

maryam4s26 commented 9 months ago

I'm not really sure what you mean by "disabling" the client exactly.

But if you just want to block input, then you can create a service provider that places a transparent element into the DOM at the max z-index that captures the events instead of the desktop/panels/windows/etc.

Could you please give me an example?

andersevenrud commented 9 months ago

Create a service provider:

// my-provider.js

export default class DisableUIServiceProvider {
  constructor(core) {
    this.core = core;
    this.$overlay = document.createElement('div');
  }

  init() {
    // Provide as an API. You can also use events for this, or some other custom stuff inside this provider.
    this.core.singleton('custom/disable-ui', () => ({
      enable: () => this.enable(),
      disable: () => this.disable(),
    }));

    this.$overlay.appendChild(document.createTextNode('UI has been disabled!'));

    Object.assign(this.$overlay.style, {
      position: 'fixed',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      backgroundColor: 'rgba(0, 0, 0, 0.5)',
      zIndex: 'calc(9e999)',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      fontSize: '40px',
      fontWeight: 'bold',
      color: '#fff',
    })
  }

  provides() {
    return [
      'custom/disable-ui'
    ]
  }

  enable() {
    if (this.$overlay.parentNode) {
      document.body.removeChild(this.$overlay);
    }
  }

  disable() {
    if (!this.$overlay.parentNode) {
      document.body.appendChild(this.$overlay);
    }
  }
}

Register:

// index.js
import DisableUIServiceProvider from './my-provider.js'

// ...
  osjs.register(DisableUIServiceProvider);
// ...

Test out in browser console:

OSjs.make('custom/disable-ui').disable(); // Disable UI
OSjs.make('custom/disable-ui').enable(); // Enable UI

This only blocks mouse events, but you can probably expand this to force focus onto a hidden input field for keyboard as well.

maryam4s26 commented 9 months ago

Thank you for your complete explanation. In fact, I wanted the background to be disabled and a dialog to be shown to the user and not allowed to do anything until the user clicked the OK button. But the dialogue is not the highest and it is not @andersevenrud

// my-provider.js

export default class SessionExpiryServiceProvider extends ServiceProvider {
  constructor(core, options = {}) {
    super(core, options);
    this.core = core;
    this.timeout = core.configuration.sessionTimeout;
    this.$overlay = document.createElement('div');
  }

  provide() {}

  async init() {
    setTimeout(() => {
      Object.assign(this.$overlay.style, {
        position: 'fixed',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        backgroundColor: 'rgba(0, 0, 0, 0.5)',
        zIndex: 'calc(9e999)',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '40px',
        fontWeight: 'bold',
        color: '#fff',
      });
      document.body.appendChild(this.$overlay);
      const args = {
        message: 'Your session has been expired. Please logout and login again.',
        title: 'Error',
        buttons: ['ok'],
      };
      this.core.make('osjs/dialog', 'confirm', args, (btn, value) => {
        if(btn === 'ok') {
          this.core.make('osjs/auth').logout();
        }
      });
    }, this.timeout);
  }
}
andersevenrud commented 9 months ago

The easiest way I can think of is just to hack it so that you move the dialog into the new overlay.

Or you could just create some basic custom UI to avoid using a hack-y solution like this.

export default class SessionExpiryServiceProvider {
  constructor(core) {
    this.core = core;
    this.$overlay = document.createElement('div');
  }

  init() {
    const sessionTimeout = this.core.config('sessionTimeout', 5000);

    Object.assign(this.$overlay.style, {
      position: 'fixed',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      backgroundColor: 'rgba(0, 0, 0, 0.5)',
      zIndex: 'calc(9e999)',
    });

    setTimeout(() => this.run(), sessionTimeout);
  }

  run() {
    this.core.$root.appendChild(this.$overlay); // core.$root is the same as document.body, but "safe"

    const args = {
      message: 'Your session has been expired. Please logout and login again.',
      title: 'Error',
      buttons: ['ok'],
    };

    const instance = this.core.make('osjs/dialog', 'confirm', args, () => {
      this.core.make('osjs/auth').logout();
    });

    // HACK: Move Window to our overlay instead of core.$contents
    this.$overlay.appendChild(instance.win.$element);
  }
}
maryam4s26 commented 9 months ago

Thank you for your complete explanation. Yes, it was completely correct.

And how can expand this to force focus onto a hidden input field for keyboard? @andersevenrud

andersevenrud commented 9 months ago

Great!

Please close your own issues after they have been resolved.

andersevenrud commented 9 months ago

I didn't see your edit. Better to post a new comment instead so I get notifications for it :)

To capture all the keyboard events you can actually just do this:

  run() {
    const args = {
      message: 'Your session has been expired. Please logout and login again.',
      title: 'Error',
      buttons: ['ok'],
    };

    const instance = this.core.make('osjs/dialog', 'confirm', args, () => {
      this.core.make('osjs/auth').logout();
    });

    // Prevent keys to be executed outside window
    for (const n of ['keydown', 'keyup', 'keypress']) {
      this.core.$root.addEventListener(n, (ev) => {
        if (!instance.win.$element.contains(ev.target)) {
          ev.preventDefault();
          ev.stopPropagation();
        }
      });
    }

    // NOTE: core.$root is the same as document.body, but "safe"
    this.core.$root.appendChild(this.$overlay);

    // HACK: Move Window to our overlay instead of core.$contents
    this.$overlay.appendChild(instance.win.$element);
  }
maryam4s26 commented 9 months ago

thanks a lot