Feature Wiki

Information about planned and released features

Tabs

Web Notifications for On-Screen-chat

1 Initial Problem

If On-screen chat is available, a user gets a chat-window down to the right and can join the On-Screen chat.

When the user works with different brower-tabs or windows, then he often don`t notice that another user started a On-Screen Chat, he does not see the chat-window.  A notification for On-Screen chats could solve this problem.

Actually there is already a sound-notification for repository-chat invitations.

2 Conceptual Summary

We suggest the following improvements:

  • when a user activates the notification in his individual chat setting
  • then he also gets an notification when a On-Screen chat starts
  • In the settings, we should add just one notification setting for both functionalities. We should not add two different notification option, because this would add additional difficulty for users, and we don`t see an additional benefit.
ILIAS-Administrators
  • can de/activate ONE setting "Play a sound" for "Repository-chats" AND "On-Screen chats."
  • located in: Administration » Chat Room » Settings » General Chat Settings » Enable Chat » Enable On-Screen-Chat
  • Name of the setting: "Play a sound"
  • with the following byline: "Play a sound when receiving a new invitation for repository chats and/or play a sound when receiving a new On-Screen-Chat conversation"
  • If user A sends a message to user B in a conversation, that is in idle state a certain amount of time, this conversation is not considered as a new conversation. We should introduce a new setting, ILIAS Administrators can change the global default-value of 5 minutes. After 5 minutes of inactivity, when a new message occurs, then the sound-notification starts again.
ILIAS-Users
  • In their individual chat settings, they first see the setting "Allow On-Screen Chat Conversation"
  • When the ILIAS-administrator activates the sound notification, then users see a second setting "Play a sound"
  • with the following  byline "Play a sound when receiving a new chat invitation"
  • Default-state of the individual "Play a sound" setting should still be: Off

3 User Interface Modifications

3.1 List of Affected Views

The following screens needs improvements:

1. ILIAS-Administration » Chat Room » Settings » General Settings

2. User-Settings » Chat Settings

3.2 User Interface Details

1. ILIAS-Administration » Chat Room » Settings » General Settings

Web Notifications have to be globally enabled by the administrator. Furthermore the administrator can define a numeric value (in minutes) for the idle time. This value defines the minimum interval at which a Web Notification is triggered for incoming messages.

2. User-Settings » Chat Settings

If globally enabled, a user can individually decide upon whether or not it wants to enable Web Notifications for the On-Screen Chat.

In case the user enables the notifications here, the browser will request permissions for sending Web Notificatons. If the user does not grant permission here, we will automatically/immediatly try to disable the setting (egain) with an appropriate MessageBox (Type: Info) after the user submits the form.

If the user does not grant permissions in the browser (because he/she added the ILIAS domain to the notification blacklist manually) and 'Notification.permission' results in 'denied' we will disable the checkbox (on the client side via JavaScript, because we cannot detect this on the server/PHP) as long as the user revoked his/her decitions in her/his browser/os settings.

Trigger for notifications on screen chat

Case 1
Technically a new conversation is created when user A starts a new conversation with user B.

(1) A starts a new conversation with user B and sends a text:
=> User B gets a notification.

Case 2
User C is added to an existing conversation of A and B: In this case a new conversation with A, B and C is created on demand.

(2) A adds C to an existing A-B conversation, and A sends a message:
=> New Chat window opens at C and B, and both get a notification.


Case 3
Conversation is in idle state (means no chat messages were sent in a certain amount of time). In idle state, no notifications are sent, except the default-value ...
Exception: In the global Administration, we suggest a default-value of 5 min: If the idle-state is bigger than x minutes, then a new text-message produces a new notification. (could be changed from Administrator in global administration settings of chat) ((Mockup)).

(3) Conversation between user A and B stopped since 10min. A sends a message:
=> User B gets a new notification (no matter if conversation window is open or not).

Case 4
No notification if User B is logged out.

(4) User B ist offline at the time, user A starts a new conversation.
=> No notification for B, when he/she logs in. He/She visually see in the glyph, that another user has started a chat.

3.3 New User Interface Concepts

No new interface elements.

4 Technical Information

4.1 Technical Challenges

4.1.1 Web Notifications

Browser API Support

For ILIAS 6.0.x we will support browsers according to their support of the APIs listed above. We will not introduce service workers to support Chome in Android.

Web Notifications should only be triggered if none of the ILIAS browser tabs is 'in focus' according to the Page Visibility API. This could be achieved by additionally using the Local Storage API to let the different browser tabs communicate with each other. 

Granting Permissions

Additionally to a global and an individual setting in ILIAS where a user can enable/disable the notifications, permissions must also be granted in the browser.

Prototype: Putting it all together

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
var storageKey = 'sendWebNotification';
var isPageHidden = false;
var sentNoti = {};
 
function sendBrowserNoti(notificationId) {
if (window.location.protocol !== "https:") {
alert("No HTTPS detected, please use https:// wo use the Web Notification API");
} else {
if ("Notification" in window) {
Notification.requestPermission().then(function (result) {
if (result === 'denied') {
alert('Permission wasn\'t granted. Please check your browser configuration.');
return;
}
 
if (result === 'default') {
alert('The permission request was dismissed.');
return;
}
 
if (!sentNoti.hasOwnProperty(notificationId)) {
sentNoti[notificationId] = true;
 
var n = new Notification("Hello, ILIAS!", {
tag: notificationId,
body: "You received a chat message ...",
icon: "./templates/default/images/icon_chta.svg"
});
n.onshow = function () {
window.setTimeout(n.close.bind(n), 20000);
};
n.onclick = function (e) {
e.preventDefault();
}.bind(n);
}
});
} else {
alert("No Notification support for your browser");
}
}
}
 
function receiveEvent(someValueFromTheChatServerIdentifyingAnEventToBeHandled) {
var randomTabValue = Math.random() * 10000,
activeTabKey = "activeTabForNoti" + someValueFromTheChatServerIdentifyingAnEventToBeHandled;
 
if (localStorage.getItem(activeTabKey) === null) {
localStorage.setItem(activeTabKey, randomTabValue);
}
 
if (localStorage.getItem(activeTabKey) === randomTabValue.toString()) {
sendBrowserNoti(someValueFromTheChatServerIdentifyingAnEventToBeHandled);
}
}
 
$(window).on('storage', function(e) {
if (
e.originalEvent.key === 'sendWebNotification' &&
typeof e.originalEvent.newValue === 'string'
) {
var ik = "ignoreWebNotification" + e.originalEvent.newValue;
console.log("Checking ingore key: " + ik);
 
if (!isPageHidden) {
localStorage.setItem("ignoreWebNotification" + e.originalEvent.newValue, "1");
console.log("Ignoring event because event receiving tab is visible: " + e.originalEvent.newValue);
} else if (localStorage.getItem(ik) === "1") {
console.log("Ignoring event because one tab marked notification as 'to be ignored': " + e.originalEvent.newValue);
} else {
console.log("Tab is invisible, no other tab seems to be visible. Delegating event ...");
receiveEvent(e.originalEvent.newValue);
}
}
});
 
function triggerWebNotificationProcess(uuid) {
if (isPageHidden) {
window.setTimeout(function() {
console.log("Propagating event because current tab is hidden: " + uuid);
 
// Emit event to all other browser tabs
localStorage.setItem(storageKey, uuid);
 
// Emit event for the current tab
var e = $.Event('storage');
e.originalEvent = {
key: storageKey,
oldValue: "oldValue",
newValue: uuid
};
$(window).trigger(e);
}, 50);
} else {
console.log("Ignoring event because current tab is visible: " + uuid);
localStorage.setItem("ignoreWebNotification" + uuid, "1");
}
}
 
document.addEventListener("visibilitychange", function() {
if (!document["hidden"]){
isPageHidden = false;
} else {
isPageHidden = true;
}
}, false):

Revoke Permissions

Automatically revoking permissions (to let the browser send you Web Notifications) could be achieved if the Permissions API is supported by most browsers in the future. This could be useful if a user decides to disable the notifications in it's personal ILIAS settings.

4.1.2 Sound Notifications

Autoplay Policy Changes

If a user has not interacted with a tab, we cannot automatically play a sound notification, see: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes

Multiple Tabs: Prevent Duplicate Sound Notificatons

The user's browser will be notified about new conversations and messages by the chat server. Client and server communicate via the HTML5 WebSocket protocol. The server does not now a 'browser tab concept', nor do browser tabs know about other browser tabs (e.g. how many ILIAS tabs are open etc.), almost simultaneously. Because each opened ILIAS tab will receive a 'New Conversation' event, we have to ensure a notification for a unique identifiable event is only played once in one of the tabs.

Scenarios:

  1. There are N > 1 opened ILIAS tabs, and one tab can be determined as 'visible' by the Page Visibility API
    • The visible tab should play the audio file, all other tabs can ignore the event.
  2. There are N opened ILIAS tabs, and the user interacts with another non ILIAS tab being in focus, e.g. http://www.ilias.de .
    • Now the N hidden ILIAS tabs have to negotiate which one should play the audio file. This could be maybe done via Local Storage API. There could be a global key (e.g. 'activeTabForNoti_' + EventId). Each tab has to generate a random string when receiving an event a sound notification should be played for. At first, the event callback method has to check whether or not an item for the global key exists in storage. If not, it will immediatelyset it's random string for this global key. Then the current value for the global key has to be requested from storage. If it equals the randomly generated string of the tab the notification will be played.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
function playAudioNotification() {
let id = 'notification_' + Math.random().toString(36).substr(2, 5),
$notielm;
 
$notielm = $('<audio id="' + id + '" autoplay></audio>');
$notielm.append($('<source src="Modules/Chatroom/sounds/receive.mp3" type="audio/mp3" />'));
$notielm.append($('<source src="Modules/Chatroom/sounds/receive.ogg" type="audio/ogg" />'));
$("#mainscrolldiv").append($notielm);
 
let p = document.getElementById(id).play();
if (p !== undefined) {
p.then(function() {
alert("Works!");
}).catch(function(e) {
alert(e);
});
}
}
 
function receiveEvent(someValueFromTheChatServerIdentifyingAnEventToBeHandled) {
let randomTabValue = Math.random() * 10000,
activeTabKey = "activeTabForNoti" + someValueFromTheChatServerIdentifyingAnEventToBeHandled;
 
if (localStorage.getItem(activeTabKey) === null) {
localStorage.setItem(activeTabKey, randomTabValue);
}
 
if (localStorage.getItem(activeTabKey) === randomTabValue.toString()) {
playAudioNotification();
}
}
 
$(window).on('storage', function(e) {
if (e.originalEvent.key === 'playChatSound' && typeof e.originalEvent.newValue === 'string') {
receiveEvent(e.originalEvent.newValue);
}
});
 
function triggerAudioNotification() {
let storageKey = 'playChatSound',
randomValue = (Math.random() * 10000).toString(); // This value has to be given by the chat server
 
// Emit event to all other browser tabs
localStorage.setItem(storageKey, randomValue);
 
// For the current tab
let e = $.Event('storage');
e.originalEvent = {
key: storageKey,
oldValue: "oldValue",
newValue: randomValue
};
$(window).trigger(e);
}
 
// Trigger events to emit event to all avtive browser tabs
triggerAudioNotification();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
function playAudioNotification() {
let id = 'notification_' + Math.random().toString(36).substr(2, 5),
$notielm;
 
$notielm = $('<audio id="' + id + '" autoplay></audio>');
$notielm.append($('<source src="Modules/Chatroom/sounds/receive.mp3" type="audio/mp3" />'));
$notielm.append($('<source src="Modules/Chatroom/sounds/receive.ogg" type="audio/ogg" />'));
$("#mainscrolldiv").append($notielm);
 
let p = document.getElementById(id).play();
if (p !== undefined) {
p.then(function() {
alert("Works!");
}).catch(function(e) {
alert(e);
});
}
}
 
function receiveEvent(someValueFromTheChatServerIdentifyingAnEventToBeHandled) {
let randomTabValue = Math.random() * 10000,
activeTabKey = "activeTabForNoti" + someValueFromTheChatServerIdentifyingAnEventToBeHandled;
 
if (localStorage.getItem(activeTabKey) === null) {
localStorage.setItem(activeTabKey, randomTabValue);
}
 
if (localStorage.getItem(activeTabKey) === randomTabValue.toString()) {
playAudioNotification();
}
}
 
$(window).on('storage', function(e) {
if (e.originalEvent.key === 'playChatSound' && typeof e.originalEvent.newValue === 'string') {
receiveEvent(e.originalEvent.newValue);
}
});
 
function triggerAudioNotification() {
let storageKey = 'playChatSound',
randomValue = (Math.random() * 10000).toString(); // This value has to be given by the chat server
 
// Emit event to all other browser tabs
localStorage.setItem(storageKey, randomValue);
 
// For the current tab
let e = $.Event('storage');
e.originalEvent = {
key: storageKey,
oldValue: "oldValue",
newValue: randomValue
};
$(window).trigger(e);
}
 
// Trigger events to emit event to all avtive browser tabs
triggerAudioNotification();

5 Contact

6 Funding

If you are interest in funding this feature, please add your name and institution to this list.

7 Discussion

Jansen, Michael [mjansen] 03 SEPT 2018: Please define exactly the trigger "when a On-Screen chat starts".

  • Please make a detailed and exact elaboration when which user should receive a sound notification, and when not.
  • Technically a new conversation is created when user A starts a new conversation with user B, or user C is added to an existing conversation of A and B. In this case a new conversation with A, B and C is created on demand. Both are new chat conversations, technically. If user A sends a message to user B in a conversation, that is in idle state a certain amount of time, this conversation is not considered as a new conversation.
  • What about a user B being offline at the time, user A starts a new conversation? Should user B receive a sound notificaton after he/she logged in into ILIAS?

Jansen, Michael [mjansen] 17 DEZ 2018: @Hanshörg: Maybe we can simplify/break down this to:

  • (If feature is enabled) Play a notification sound if the first message of a (new) conversation is received.
  • (if features is enabled and idle-time is configured) Play a notification sound if the user receives a new message and the configured idle-time value is exceeded. The idle time is resetted whenever a message was received or sent by the user.
This is much easier to understand (e.g. for the Jour Fixe) and to check (against acceptance criteria).

JourFixe, ILIAS [jourfixe], 07 JAN 2019 : We highly appreciate this suggestion and schedule it for 6.0 but please use the Web Application API (see https://www.w3.org/TR/notifications/) for this scenario - even if the support of audio notification is not reliable. We believe that using a standard for this use case is more sustainable than trying to find an individual solution that might not work with future browser versions.

Jansen, Michael [mjansen], 18 JAN 2019: We did some further analysis and prototyping regarding Web Notifications. We are not convinced that the integration is a good idea (with the current state of development, browser specific behaviours etc.) in regards of end user usability. Why do we think so?

1. Presentation: The presentation of a web notifications varies depending on the the user's browser. Beside the mandatory title attribute (no HTML possible) a web notification can be optionally created with a body (no HTML possible) and an icon. We did some tests with different browsers, and the icon is only displayed in Firefox. The Chrome and Edge browsers do not display the icon, but use some kind of (ugly) 'placeholder'.

2. Life Cycle and Interactions: What we can do with the API is to automatically close a Web Notification after a certain amount of time via JavaScript when the 'shown' event is raised. In some browsers, a Web Notification is closed by the browser itself automatically, and we/ILIAS cannot influence this in any way (read or manipulate the behaviour). Furthermore Web Notifications support 'click' events. What should happen if a user clicks on a notification displayed by the browser? It is not possible to just focus the hidden browser tab the notification was created in. That's what an end user maybe expects. We just could open another window with 'window.open("https://myilias.de");', but we think this is very annoying if there are already different (hidden) tabs. And, what ILIAS page should be shown? The 'Personal Desktop', any other page?

3. Permisson Policies: First of all using Web Notifications requires an ILIAS installation to strictly use SSL (which is good). If a user never decides upon Web Notifications in ILIAS, ILIAS MUST request permission via the JavaScript API. There can be the following results/states: 1. 'granted' : ILIAS is able to create a Web Notification / 2. 'default': The initial state. ILIAS is not able to create a Web Notificaton. / 3. 'denied': ILIAS is not able to create a Web Notification.
If the user made a decision once ('granted' or 'denied'), we cannot/are not able to request the permissions again. In regards of end user usability this is not ideal. If the user wants to change it's decision made in the past, it has to do this in it's browser or operating system settings (which is different on each browser/os) and maybe enable/disable the web notifications or add/remove the ILIAS origin/URL to/from a blacklist. Furthermore this permission decision collides with an account specific setting 'Enable Notifications'.  Of course we can use the API if a user wants to receive notifications, and suppress the usage of the API if it does not. But: The decision made in the browser is not and cannot be related to an ILIAS user account. This means: Let's suppose the user owns (why ever) mutitple ILIAS accounts A, B and C. For account A the user wants to receive Web Notifications (ILIAS setting), for account B and C the user does not want to receive Web Notifications. This collides with a central browser setting working independently of any 'Account Concept' in ILIAS. Additionally a user with enabled Web Notifications in ILIAS can login with the same account on different browsers D and E, where the user granted permissions in browser D, but denied on browser E.
We think that this will cause a lot of confusion, bug reports, problems with running our TestRail tests suites etc..

Firefox
Opera
Chrome
Edge

Maybe a two-phased approach could be the best option/approach: First try to play a sound. If the promise transits to 'rejected' state (failure, a browser is not able to automatically play a sound), we could use the Web Notification (with all it's cons) as a fallback. What do you think?

8 Implementation

Lauener, Hansjörg [lauener], 7.5.2019: Browser-Notifications works with Firefox, Chrome, Safari, Edge. Sound-notification only with Firefox.

Supportpages
https://support.mozilla.org/de/kb/push-benachrichtigungen-firefox?as=u&utm_source=inproduct
https://support.apple.com/de-de/guide/safari/sfri40734/mac
https://support.apple.com/de-ch/guide/safari/customize-website-notifications-sfri40734/mac
https://support.google.com/chrome/answer/3220216?co=GENIE.Platform%3DDesktop&hl=de
https://blogs.windows.com/msedgedev/2016/05/16/web-notifications-microsoft-edge/

see also:
https://caniuse.com/#search=Web%20Notifications

Implemented as describe above with tiny changes in regargs of bylines.

Test Cases

Test cases completed at {date} by Lauener, Hansjörg [lauener]

  • C25216 : On-Screen-Chat Benachrichtigungseinstellungen
  • C25217: On-Screen-Chat Benachrichtigungseinstellungen (nicht) aktivieren
  • C25218: On-Screen-Chat Benachrichtigungseinstellungen aktivieren
  • C25219: On-Screen-Chat Benachrichtigungen erhalten

Approval

Approved at 7.5.2019 by Lauener, Hansjörg [lauener].

Last edited: 8. May 2019, 18:20, Jansen, Michael [mjansen]