Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
solid-xmpp-chat
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Components
solid-xmpp-chat
Merge requests
!113
History, Reply to messages and RAI
Code
Review changes
Check out branch
Download
Patches
Plain diff
Merged
History, Reply to messages and RAI
beta
into
master
Overview
0
Commits
39
Pipelines
7
Changes
1
Merged
Emmanuel Vodor
requested to merge
beta
into
master
4 years ago
Overview
0
Commits
39
Pipelines
7
Changes
1
Expand
Contains
History loading various fixes
Reply button on messages (mucs and chats)
Replaced the original RAI implementation with the converse one
Closes
#262 (closed)
Edited
4 years ago
by
Emmanuel Vodor
0
0
Merge request reports
Viewing commit
3fe6a3c8
Prev
Next
Show latest version
1 file
+
1
−
1
Inline
Compare changes
Side-by-side
Inline
Show whitespace changes
Show one file at a time
3fe6a3c8
register handler on all messages
· 3fe6a3c8
ubermanu
authored
4 years ago
src/plugins/converse-rai.js
+
140
−
177
Options
(
function
()
{
let
Strophe
,
$iq
,
$msg
,
$pres
,
$build
,
b64_sha1
,
_
,
Backbone
,
dayjs
,
_converse
;
let
interestingServers
=
new
Set
();
let
subscribedServers
=
new
Set
();
converse
.
plugins
.
add
(
'
converse-rai
'
,
{
'
dependencies
'
:
[],
'
initialize
'
:
function
()
{
_converse
=
this
.
_converse
;
Strophe
=
converse
.
env
.
Strophe
;
$iq
=
converse
.
env
.
$iq
;
$msg
=
converse
.
env
.
$msg
;
$pres
=
converse
.
env
.
$pres
;
$build
=
converse
.
env
.
$build
;
b64_sha1
=
converse
.
env
.
b64_sha1
;
_
=
converse
.
env
.
_
;
Backbone
=
converse
.
env
.
Backbone
;
dayjs
=
converse
.
env
.
dayjs
;
_converse
.
api
.
settings
.
extend
({
rai_notification
:
true
,
rai_notification_label
:
'
Room Activity Indicator
'
,
});
_converse
.
api
.
listen
.
on
(
'
connected
'
,
function
()
{
setupRoomActivityIndicators
();
});
_converse
.
api
.
listen
.
on
(
'
chatRoomViewInitialized
'
,
function
(
view
)
{
const
jid
=
view
.
model
.
get
(
'
jid
'
);
if
(
view
.
model
.
get
(
'
num_unread
'
)
>
0
||
view
.
model
.
get
(
'
num_unread_general
'
)
>
0
)
{
emitNotification
(
jid
);
/**
* Add RAI support to MUCs.
* @see https://xmpp.org/extensions/inbox/room-activity-indicators.html
*/
converse
.
plugins
.
add
(
'
converse-rai
'
,
{
dependencies
:
[
'
converse-chatboxes
'
,
],
overrides
:
{
ChatRoom
:
{
async
initialize
()
{
await
this
.
__super__
.
initialize
.
apply
(
this
,
arguments
);
this
.
listenTo
(
this
,
'
change:hidden
'
,
this
.
onHiddenChange
);
},
/**
* Handler that gets called when the 'hidden' flag is toggled.
* @private
* @method _converse.ChatRoom#onHiddenChange
*/
async
onHiddenChange
()
{
const
conn_status
=
this
.
session
.
get
(
'
connection_status
'
);
const
{
api
}
=
this
.
__super__
.
_converse
;
if
(
this
.
get
(
'
hidden
'
)
&&
conn_status
===
converse
.
ROOMSTATUS
.
ENTERED
&&
api
.
settings
.
get
(
'
muc_subscribe_to_rai
'
)
// && this.getOwnAffiliation() !== 'none'
)
{
this
.
sendMarkerForLastMessage
(
'
received
'
,
true
);
await
this
.
leave
();
await
this
.
close
();
}
else
if
(
conn_status
===
converse
.
ROOMSTATUS
.
DISCONNECTED
)
{
await
this
.
rejoin
();
}
});
_converse
.
api
.
listen
.
on
(
'
raiRoomsUpdated
'
,
function
(
rooms
)
{
interestingServers
=
new
Set
(
rooms
.
filter
(
room
=>
room
).
map
(
Strophe
.
getDomainFromJid
));
if
(
_converse
.
api
.
connection
.
connected
())
{
updateSubscriptions
();
},
/**
* Subscribe to RAI if connected to the room, but it's hidden.
* @private
* @method _converse.ChatRoom#onConnectionStatusChanged
*/
async
onConnectionStatusChanged
()
{
const
conn_status
=
this
.
session
.
get
(
'
connection_status
'
);
const
{
api
}
=
this
.
__super__
.
_converse
;
if
(
this
.
get
(
'
hidden
'
)
&&
conn_status
===
converse
.
ROOMSTATUS
.
ENTERED
&&
api
.
settings
.
get
(
'
muc_subscribe_to_rai
'
)
// && this.getOwnAffiliation() !== 'none'
)
{
await
this
.
leave
();
await
this
.
close
();
this
.
enableRAI
();
}
else
{
await
this
.
__super__
.
onConnectionStatusChanged
.
apply
(
this
,
arguments
);
}
});
_converse
.
api
.
listen
.
on
(
'
chatBoxScrolledDown
'
,
function
(
view
)
{
const
jid
=
view
.
chatbox
.
get
(
'
jid
'
);
const
id_attr
=
'
stanza_id
'
+
jid
;
const
messages
=
view
.
chatbox
.
messages
;
if
(
!
getUnreadStatus
(
jid
))
{
return
;
},
/**
* Ensures that the user is subscribed to XEP-0437 Room Activity Indicators
* if `muc_subscribe_to_rai` is set to `true`.
* Only affiliated users can subscribe to RAI, but this method doesn't
* check whether the current user is affiliated because it's intended to be
* called after the MUC has been left and we don't have that information
* anymore.
* @private
* @method _converse.ChatRoom#enableRAI
*/
enableRAI
()
{
const
{
api
}
=
this
.
__super__
.
_converse
;
if
(
api
.
settings
.
get
(
'
muc_subscribe_to_rai
'
))
{
api
.
rooms
.
subscribe
(
this
.
get
(
'
jid
'
));
}
},
},
ChatBox
:
{
/**
* Finds the last eligible message and then sends a XEP-0333 chat marker for it.
* @param { ('received'|'displayed'|'acknowledged') } [type='displayed']
* @param { Boolean } force - Whether a marker should be sent for the
* message, even if it didn't include a `markable` element.
*/
sendMarkerForLastMessage
(
type
=
'
displayed
'
,
force
=
false
)
{
const
msgs
=
Array
.
from
(
this
.
messages
.
models
);
msgs
.
reverse
();
const
msg
=
msgs
.
find
(
m
=>
m
.
get
(
'
sender
'
)
===
'
them
'
&&
(
force
||
m
.
get
(
'
is_markable
'
)));
msg
&&
this
.
sendMarkerForMessage
(
msg
,
type
,
force
);
},
},
},
initialize
()
{
const
{
Strophe
,
_
,
$pres
,
u
,
sizzle
,
log
}
=
converse
.
env
;
const
_converse
=
this
.
_converse
;
const
{
api
}
=
_converse
;
for
(
let
i
=
messages
.
length
-
1
;
i
>=
0
;
i
--
)
{
const
message
=
messages
.
at
(
i
);
if
(
message
.
has
(
id_attr
))
{
let
id
=
message
.
get
(
id_attr
);
if
(
id
!=
sessionStorage
.
getItem
(
'
rai_displayed.
'
+
jid
))
{
sessionStorage
.
setItem
(
'
rai_displayed.
'
+
jid
,
id
);
setUnreadStatus
(
jid
,
false
);
setTimeout
(()
=>
sendMarker
(
jid
,
id
,
'
displayed
'
),
0
);
}
break
;
}
}
});
Strophe
.
addNamespace
(
'
RAI
'
,
'
urn:xmpp:rai:0
'
);
_converse
.
api
.
listen
.
on
(
'
chatBoxInsertedIntoDOM
'
,
function
(
view
)
{
const
jid
=
view
.
model
.
get
(
'
jid
'
);
api
.
settings
.
extend
({
muc_subscribe_to_rai
:
false
,
});
if
(
view
.
model
.
get
(
'
num_unread
'
)
>
0
)
{
emitNotification
(
jid
);
Object
.
assign
(
api
.
rooms
,
{
/**
* Send an RAI stanza for the given jids.
* The presence stanza is sent for a whole muc domain.
*
* @param {string|Array} jids
* @returns {void}
*/
subscribe
(
jids
)
{
if
(
!
api
.
settings
.
get
(
'
muc_subscribe_to_rai
'
))
{
log
.
error
(
`You must enable the 'muc_subscribe_to_rai' option before subscribing to a MUC.`
);
return
;
}
});
_converse
.
api
.
listen
.
on
(
'
message
'
,
function
(
data
)
{
var
chatbox
=
data
.
chatbox
;
var
history
=
data
.
attrs
.
is_archived
;
var
sender
=
data
.
attrs
.
sender
;
var
body
=
data
.
attrs
.
body
;
if
(
!
history
&&
body
&&
chatbox
&&
sender
!==
'
me
'
)
{
const
alert
=
chatbox
.
get
(
'
num_unread
'
)
>
0
;
const
notify
=
chatbox
.
get
(
'
num_unread_general
'
)
>
0
;
if
(
alert
||
notify
)
{
emitNotification
(
chatbox
.
get
(
'
jid
'
),
alert
);
}
if
(
typeof
jids
===
'
string
'
)
{
jids
=
[
jids
];
}
});
},
});
function
subscribeServer
(
server_name
)
{
const
id
=
Math
.
random
().
toString
(
36
).
substr
(
2
,
9
);
_converse
.
connection
.
send
(
converse
.
env
.
$pres
({
to
:
server_name
,
id
:
id
,
}).
c
(
'
rai
'
,
{
'
xmlns
'
:
'
xmpp:prosody.im/protocol/rai
'
,
}));
}
const
muc_domains
=
jids
.
map
(
jid
=>
Strophe
.
getDomainFromJid
(
jid
));
function
unsubscribeServer
(
server_name
)
{
_converse
.
connection
.
send
(
converse
.
env
.
$pres
({
to
:
server_name
,
type
:
'
unavailable
'
,
}).
c
(
'
rai
'
,
{
'
xmlns
'
:
'
xmpp:prosody.im/protocol/rai
'
,
}));
}
function
updateSubscriptions
()
{
var
new_servers
=
new
Set
([...
interestingServers
].
filter
(
server
=>
!
subscribedServers
.
has
(
server
)));
var
obsolete_servers
=
new
Set
([...
subscribedServers
].
filter
(
server
=>
!
interestingServers
.
has
(
server
)));
for
(
let
server
of
obsolete_servers
)
{
unsubscribeServer
(
server
);
}
for
(
let
server
of
new_servers
)
{
subscribeServer
(
server
);
}
}
_
.
uniq
(
muc_domains
).
forEach
(
muc_domain
=>
{
const
rai
=
$pres
({
to
:
muc_domain
,
id
:
u
.
getUniqueId
()
}).
c
(
'
rai
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
RAI
,
});
api
.
send
(
rai
);
log
.
debug
(
`Sent RAI stanza for domain "
${
muc_domain
}
"`
);
});
},
});
function
setupRoomActivityIndicators
()
{
updateSubscriptions
();
// If we already have unread notifications stored for this session, emit them now
for
(
var
i
=
0
;
i
<
sessionStorage
.
length
;
i
++
)
{
if
(
sessionStorage
.
key
(
i
).
indexOf
(
'
rai_notify.
'
)
==
0
)
{
const
jid
=
sessionStorage
.
key
(
i
).
substring
(
11
);
emitNotification
(
jid
);
/**
* Triggers an event if a jid has activity.
*
* @param message
* @returns {boolean}
*/
function
mucActivityHandler
(
message
)
{
const
rai
=
sizzle
(
`rai[xmlns="
${
Strophe
.
NS
.
RAI
}
"]`
,
message
).
pop
();
if
(
rai
)
{
rai
.
querySelectorAll
(
'
activity
'
).
forEach
(
activity
=>
{
const
jid
=
activity
.
textContent
;
log
.
debug
(
`Received activity for the room:
${
jid
}
`
);
api
.
trigger
(
'
chatRoomHasActivity
'
,
jid
);
});
}
}
// Listen for incoming RAI from the server
_converse
.
connection
.
addHandler
(
function
(
message
)
{
const
from_jid
=
message
.
attributes
.
from
?.
nodevalue
;
const
room_jid
=
from_jid
?.
split
(
'
/
'
)[
0
];
const
room
=
''
;
let
ignore
=
false
;
for
(
let
i
=
0
;
i
<
_converse
.
chatboxes
.
models
.
length
;
i
++
)
{
if
(
_converse
.
chatboxes
.
models
[
i
].
id
===
room_jid
)
{
room
=
_converse
.
chatboxes
.
models
[
i
].
id
;
break
;
}
}
if
(
room
&&
from_jid
&&
room_jid
)
{
if
(
from_jid
===
room_jid
+
'
/
'
+
room
.
get
(
'
nick
'
))
{
ignore
=
true
;
}
}
if
(
message
&&
!
ignore
)
message
.
querySelectorAll
(
'
activity
'
).
forEach
(
function
(
activity
)
{
if
(
activity
&&
activity
.
namespaceURI
==
'
xmpp:prosody.im/protocol/rai
'
)
{
const
jid
=
activity
.
textContent
;
setUnreadStatus
(
jid
,
true
);
emitNotification
(
jid
);
}
});
return
true
;
},
null
,
'
message
'
);
}
function
setUnreadStatus
(
jid
,
flag
)
{
if
(
flag
)
{
sessionStorage
.
setItem
(
'
rai_notify.
'
+
jid
,
'
true
'
);
}
else
{
sessionStorage
.
removeItem
(
'
rai_notify.
'
+
jid
);
}
}
function
getUnreadStatus
(
jid
)
{
return
sessionStorage
.
getItem
(
'
rai_notify.
'
+
jid
)
==
'
true
'
;
}
function
emitNotification
(
jid
,
alert
)
{
_converse
.
api
.
trigger
(
'
chatRoomActivityIndicators
'
,
jid
);
}
function
sendMarker
(
to_jid
,
id
,
type
)
{
const
stanza
=
converse
.
env
.
$msg
({
'
from
'
:
_converse
.
connection
.
jid
,
'
id
'
:
Math
.
random
().
toString
(
36
).
substr
(
2
,
9
),
'
to
'
:
to_jid
,
'
type
'
:
'
groupchat
'
,
}).
c
(
type
,
{
'
xmlns
'
:
converse
.
env
.
Strophe
.
NS
.
MARKERS
,
'
id
'
:
id
,
// Register our RAI handler
api
.
listen
.
on
(
'
connected
'
,
()
=>
{
_converse
.
connection
.
addHandler
(
mucActivityHandler
,
null
,
'
message
'
);
});
_converse
.
api
.
send
(
stanza
);
}
})();
},
});
Loading