Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
converse.js
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
converse.js
Commits
e3ebde97
Commit
e3ebde97
authored
Dec 04, 2020
by
JC Brand
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move converse-chat plugin into folder
parent
01e03fc6
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1577 additions
and
1518 deletions
+1577
-1518
package-lock.json
package-lock.json
+10
-10
spec/corrections.js
spec/corrections.js
+1
-1
src/headless/headless.js
src/headless/headless.js
+1
-1
src/headless/plugins/chat.js
src/headless/plugins/chat.js
+0
-1504
src/headless/plugins/chat/api.js
src/headless/plugins/chat/api.js
+144
-0
src/headless/plugins/chat/index.js
src/headless/plugins/chat/index.js
+203
-0
src/headless/plugins/chat/message.js
src/headless/plugins/chat/message.js
+239
-0
src/headless/plugins/chat/model-with-contact.js
src/headless/plugins/chat/model-with-contact.js
+23
-0
src/headless/plugins/chat/model.js
src/headless/plugins/chat/model.js
+951
-0
src/headless/plugins/muc.js
src/headless/plugins/muc.js
+5
-2
No files found.
package-lock.json
View file @
e3ebde97
...
...
@@ -4482,9 +4482,9 @@
}
},
"@octokit/openapi-types"
:
{
"version"
:
"
1.2.2
"
,
"resolved"
:
"https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-
1.2.2
.tgz"
,
"integrity"
:
"sha512-
vrKDLd/Rq4IE16oT+jJkDBx0r29NFkdkU8GwqVSP4RajsAvP23CMGtFhVK0pedUhAiMvG1bGnFcTC/xCKaKgm
w=="
,
"version"
:
"
2.0.0
"
,
"resolved"
:
"https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-
2.0.0
.tgz"
,
"integrity"
:
"sha512-
J4bfM7lf8oZvEAdpS71oTvC1ofKxfEZgU5vKVwzZKi4QPiL82udjpseJwxPid9Pu2FNmyRQOX4iEj6W1iOSnP
w=="
,
"dev"
:
true
},
"@octokit/plugin-enterprise-rest"
:
{
...
...
@@ -4628,12 +4628,12 @@
}
},
"@octokit/types"
:
{
"version"
:
"6.
0.3
"
,
"resolved"
:
"https://registry.npmjs.org/@octokit/types/-/types-6.
0.3
.tgz"
,
"integrity"
:
"sha512-
6y0Emzp+uPpdC5QLzUY1YRklvqiZBMTOz2ByhXdmTFlc3lNv8Mi28dX1U1b4scNtFMUa3tkpjofNFJ5NqMJaZw
=="
,
"version"
:
"6.
1.0
"
,
"resolved"
:
"https://registry.npmjs.org/@octokit/types/-/types-6.
1.0
.tgz"
,
"integrity"
:
"sha512-
bMWBmg77MQTiRkOVyf50qK3QECWOEy43rLy/6fTWZ4HEwAhNfqzMcjiBDZAowkILwTrFvzE1CpP6gD0MuPHS+A
=="
,
"dev"
:
true
,
"requires"
:
{
"@octokit/openapi-types"
:
"^
1.2
.0"
,
"@octokit/openapi-types"
:
"^
2.0
.0"
,
"@types/node"
:
">= 8"
}
},
...
...
@@ -22471,9 +22471,9 @@
},
"dependencies"
:
{
"ws"
:
{
"version"
:
"7.4.
0
"
,
"resolved"
:
"https://registry.npmjs.org/ws/-/ws-7.4.
0
.tgz"
,
"integrity"
:
"sha512-
kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4N
Q=="
,
"version"
:
"7.4.
1
"
,
"resolved"
:
"https://registry.npmjs.org/ws/-/ws-7.4.
1
.tgz"
,
"integrity"
:
"sha512-
pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGF
Q=="
,
"optional"
:
true
}
}
...
...
spec/corrections.js
View file @
e3ebde97
...
...
@@ -80,7 +80,7 @@ describe("A Chat Message", function () {
await
u
.
waitUntil
(()
=>
(
u
.
hasClass
(
'
correcting
'
,
view
.
el
.
querySelector
(
'
.chat-msg
'
))
===
false
),
500
);
// Test that pressing the down arrow cancels message correction
expect
(
textarea
.
value
).
toBe
(
''
);
await
u
.
waitUntil
(()
=>
textarea
.
value
===
''
)
view
.
onKeyDown
({
target
:
textarea
,
keyCode
:
38
// Up arrow
...
...
src/headless/headless.js
View file @
e3ebde97
...
...
@@ -7,7 +7,7 @@ import "./plugins/bookmarks.js"; // XEP-0199 XMPP Ping
import
"
./plugins/bosh.js
"
;
// XEP-0206 BOSH
import
"
./plugins/caps.js
"
;
// XEP-0115 Entity Capabilities
import
"
./plugins/carbons.js
"
;
// XEP-0280 Message Carbons
import
"
./plugins/chat.js
"
;
// RFC-6121 Instant messaging
import
"
./plugins/chat
/index
.js
"
;
// RFC-6121 Instant messaging
import
"
./plugins/chatboxes.js
"
;
import
"
./plugins/disco.js
"
;
// XEP-0030 Service discovery
import
"
./plugins/headlines.js
"
;
// Support for headline messages
...
...
src/headless/plugins/chat.js
deleted
100644 → 0
View file @
01e03fc6
This diff is collapsed.
Click to expand it.
src/headless/plugins/chat/api.js
0 → 100644
View file @
e3ebde97
import
{
_converse
,
api
}
from
"
../../core.js
"
;
import
log
from
"
../../log.js
"
;
export
default
{
/**
* The "chats" namespace (used for one-on-one chats)
*
* @namespace api.chats
* @memberOf api
*/
chats
:
{
/**
* @method api.chats.create
* @param {string|string[]} jid|jids An jid or array of jids
* @param {object} [attrs] An object containing configuration attributes.
*/
async
create
(
jids
,
attrs
)
{
if
(
typeof
jids
===
'
string
'
)
{
if
(
attrs
&&
!
attrs
?.
fullname
)
{
const
contact
=
await
api
.
contacts
.
get
(
jids
);
attrs
.
fullname
=
contact
?.
attributes
?.
fullname
;
}
const
chatbox
=
api
.
chats
.
get
(
jids
,
attrs
,
true
);
if
(
!
chatbox
)
{
log
.
error
(
"
Could not open chatbox for JID:
"
+
jids
);
return
;
}
return
chatbox
;
}
if
(
Array
.
isArray
(
jids
))
{
return
Promise
.
all
(
jids
.
forEach
(
async
jid
=>
{
const
contact
=
await
api
.
contacts
.
get
(
jids
);
attrs
.
fullname
=
contact
?.
attributes
?.
fullname
;
return
api
.
chats
.
get
(
jid
,
attrs
,
true
).
maybeShow
();
}));
}
log
.
error
(
"
chats.create: You need to provide at least one JID
"
);
return
null
;
},
/**
* Opens a new one-on-one chat.
*
* @method api.chats.open
* @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
* @param {Boolean} [attrs.minimized] - Should the chat be created in minimized state.
* @param {Boolean} [force=false] - By default, a minimized
* chat won't be maximized (in `overlayed` view mode) and in
* `fullscreen` view mode a newly opened chat won't replace
* another chat already in the foreground.
* Set `force` to `true` if you want to force the chat to be
* maximized or shown.
* @returns {Promise} Promise which resolves with the
* _converse.ChatBox representing the chat.
*
* @example
* // To open a single chat, provide the JID of the contact you're chatting with in that chat:
* converse.plugins.add('myplugin', {
* initialize: function() {
* const _converse = this._converse;
* // Note, buddy@example.org must be in your contacts roster!
* api.chats.open('buddy@example.com').then(chat => {
* // Now you can do something with the chat model
* });
* }
* });
*
* @example
* // To open an array of chats, provide an array of JIDs:
* converse.plugins.add('myplugin', {
* initialize: function () {
* const _converse = this._converse;
* // Note, these users must first be in your contacts roster!
* api.chats.open(['buddy1@example.com', 'buddy2@example.com']).then(chats => {
* // Now you can do something with the chat models
* });
* }
* });
*/
async
open
(
jids
,
attrs
,
force
)
{
if
(
typeof
jids
===
'
string
'
)
{
const
chat
=
await
api
.
chats
.
get
(
jids
,
attrs
,
true
);
if
(
chat
)
{
return
chat
.
maybeShow
(
force
);
}
return
chat
;
}
else
if
(
Array
.
isArray
(
jids
))
{
return
Promise
.
all
(
jids
.
map
(
j
=>
api
.
chats
.
get
(
j
,
attrs
,
true
).
then
(
c
=>
c
&&
c
.
maybeShow
(
force
)))
.
filter
(
c
=>
c
)
);
}
const
err_msg
=
"
chats.open: You need to provide at least one JID
"
;
log
.
error
(
err_msg
);
throw
new
Error
(
err_msg
);
},
/**
* Retrieves a chat or all chats.
*
* @method api.chats.get
* @param {String|string[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com']
* @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model.
* @param {Boolean} [create=false] - Whether the chat should be created if it's not found.
* @returns { Promise<_converse.ChatBox> }
*
* @example
* // To return a single chat, provide the JID of the contact you're chatting with in that chat:
* const model = await api.chats.get('buddy@example.com');
*
* @example
* // To return an array of chats, provide an array of JIDs:
* const models = await api.chats.get(['buddy1@example.com', 'buddy2@example.com']);
*
* @example
* // To return all open chats, call the method without any parameters::
* const models = await api.chats.get();
*
*/
async
get
(
jids
,
attrs
=
{},
create
=
false
)
{
async
function
_get
(
jid
)
{
let
model
=
await
api
.
chatboxes
.
get
(
jid
);
if
(
!
model
&&
create
)
{
model
=
await
api
.
chatboxes
.
create
(
jid
,
attrs
,
_converse
.
ChatBox
);
}
else
{
model
=
(
model
&&
model
.
get
(
'
type
'
)
===
_converse
.
PRIVATE_CHAT_TYPE
)
?
model
:
null
;
if
(
model
&&
Object
.
keys
(
attrs
).
length
)
{
model
.
save
(
attrs
);
}
}
return
model
;
}
if
(
jids
===
undefined
)
{
const
chats
=
await
api
.
chatboxes
.
get
();
return
chats
.
filter
(
c
=>
(
c
.
get
(
'
type
'
)
===
_converse
.
PRIVATE_CHAT_TYPE
));
}
else
if
(
typeof
jids
===
'
string
'
)
{
return
_get
(
jids
);
}
return
Promise
.
all
(
jids
.
map
(
jid
=>
_get
(
jid
)));
}
}
}
src/headless/plugins/chat/index.js
0 → 100644
View file @
e3ebde97
/**
* @module converse-chat
* @copyright 2020, the Converse.js contributors
* @license Mozilla Public License (MPLv2)
*/
import
ChatBox
from
'
./model.js
'
;
import
MessageMixin
from
'
./message.js
'
;
import
ModelWithContact
from
'
./model-with-contact.js
'
;
import
chat_api
from
'
./api.js
'
;
import
log
from
'
../../log.js
'
;
import
st
from
'
../../utils/stanza
'
;
import
{
Collection
}
from
"
@converse/skeletor/src/collection
"
;
import
{
_converse
,
api
,
converse
}
from
'
../../core.js
'
;
const
{
Strophe
,
sizzle
,
utils
}
=
converse
.
env
;
const
u
=
converse
.
env
.
utils
;
async
function
handleErrorMessage
(
stanza
)
{
const
from_jid
=
Strophe
.
getBareJidFromJid
(
stanza
.
getAttribute
(
'
from
'
));
if
(
utils
.
isSameBareJID
(
from_jid
,
_converse
.
bare_jid
))
{
return
;
}
const
chatbox
=
await
api
.
chatboxes
.
get
(
from_jid
);
chatbox
?.
handleErrorMessageStanza
(
stanza
);
}
converse
.
plugins
.
add
(
'
converse-chat
'
,
{
/* Optional dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
* this plugin. They are called "optional" because they might not be
* available, in which case any overrides applicable to them will be
* ignored.
*
* It's possible however to make optional dependencies non-optional.
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*
* NB: These plugins need to have already been loaded via require.js.
*/
dependencies
:
[
'
converse-chatboxes
'
,
'
converse-disco
'
],
initialize
()
{
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
Object
.
assign
(
api
,
chat_api
);
// Configuration values for this plugin
// ====================================
// Refer to docs/source/configuration.rst for explanations of these
// configuration settings.
api
.
settings
.
extend
({
'
allow_message_corrections
'
:
'
all
'
,
'
allow_message_retraction
'
:
'
all
'
,
'
allow_message_styling
'
:
true
,
'
auto_join_private_chats
'
:
[],
'
clear_messages_on_reconnection
'
:
false
,
'
filter_by_resource
'
:
false
,
'
send_chat_state_notifications
'
:
true
});
_converse
.
Message
=
ModelWithContact
.
extend
(
MessageMixin
);
_converse
.
Messages
=
Collection
.
extend
({
model
:
_converse
.
Message
,
comparator
:
'
time
'
});
_converse
.
ChatBox
=
ChatBox
;
/**
* Handler method for all incoming single-user chat "message" stanzas.
* @private
* @method _converse#handleMessageStanza
* @param { MessageAttributes } attrs - The message attributes
*/
_converse
.
handleMessageStanza
=
async
function
(
stanza
)
{
if
(
st
.
isServerMessage
(
stanza
))
{
// Prosody sends headline messages with type `chat`, so we need to filter them out here.
const
from
=
stanza
.
getAttribute
(
'
from
'
);
return
log
.
info
(
`handleMessageStanza: Ignoring incoming server message from JID:
${
from
}
`
);
}
const
attrs
=
await
st
.
parseMessage
(
stanza
,
_converse
);
if
(
u
.
isErrorObject
(
attrs
))
{
attrs
.
stanza
&&
log
.
error
(
attrs
.
stanza
);
return
log
.
error
(
attrs
.
message
);
}
const
has_body
=
!!
sizzle
(
`body, encrypted[xmlns="
${
Strophe
.
NS
.
OMEMO
}
"]`
,
stanza
).
length
;
const
chatbox
=
await
api
.
chats
.
get
(
attrs
.
contact_jid
,
{
'
nickname
'
:
attrs
.
nick
},
has_body
);
await
chatbox
?.
queueMessage
(
attrs
);
/**
* @typedef { Object } MessageData
* An object containing the original message stanza, as well as the
* parsed attributes.
* @property { XMLElement } stanza
* @property { MessageAttributes } stanza
* @property { ChatBox } chatbox
*/
const
data
=
{
stanza
,
attrs
,
chatbox
};
/**
* Triggered when a message stanza is been received and processed.
* @event _converse#message
* @type { object }
* @property { module:converse-chat~MessageData } data
*/
api
.
trigger
(
'
message
'
,
data
);
};
function
registerMessageHandlers
()
{
_converse
.
connection
.
addHandler
(
stanza
=>
{
if
(
sizzle
(
`message > result[xmlns="
${
Strophe
.
NS
.
MAM
}
"]`
,
stanza
).
pop
())
{
// MAM messages are handled in converse-mam.
// We shouldn't get MAM messages here because
// they shouldn't have a `type` attribute.
log
.
warn
(
`Received a MAM message with type "chat".`
);
return
true
;
}
_converse
.
handleMessageStanza
(
stanza
);
return
true
;
},
null
,
'
message
'
,
'
chat
'
);
_converse
.
connection
.
addHandler
(
stanza
=>
{
// Message receipts are usually without the `type` attribute. See #1353
if
(
stanza
.
getAttribute
(
'
type
'
)
!==
null
)
{
// TODO: currently Strophe has no way to register a handler
// for stanzas without a `type` attribute.
// We could update it to accept null to mean no attribute,
// but that would be a backward-incompatible change
return
true
;
// Gets handled above.
}
_converse
.
handleMessageStanza
(
stanza
);
return
true
;
},
Strophe
.
NS
.
RECEIPTS
,
'
message
'
);
_converse
.
connection
.
addHandler
(
stanza
=>
{
handleErrorMessage
(
stanza
);
return
true
;
},
null
,
'
message
'
,
'
error
'
);
}
function
autoJoinChats
()
{
// Automatically join private chats, based on the
// "auto_join_private_chats" configuration setting.
api
.
settings
.
get
(
'
auto_join_private_chats
'
).
forEach
(
jid
=>
{
if
(
_converse
.
chatboxes
.
where
({
'
jid
'
:
jid
}).
length
)
{
return
;
}
if
(
typeof
jid
===
'
string
'
)
{
api
.
chats
.
open
(
jid
);
}
else
{
log
.
error
(
'
Invalid jid criteria specified for "auto_join_private_chats"
'
);
}
});
/**
* Triggered once any private chats have been automatically joined as
* specified by the `auto_join_private_chats` setting.
* See: https://conversejs.org/docs/html/configuration.html#auto-join-private-chats
* @event _converse#privateChatsAutoJoined
* @example _converse.api.listen.on('privateChatsAutoJoined', () => { ... });
* @example _converse.api.waitUntil('privateChatsAutoJoined').then(() => { ... });
*/
api
.
trigger
(
'
privateChatsAutoJoined
'
);
}
/************************ BEGIN Route Handlers ************************/
function
openChat
(
jid
)
{
if
(
!
utils
.
isValidJID
(
jid
))
{
return
log
.
warn
(
`Invalid JID "
${
jid
}
" provided in URL fragment`
);
}
api
.
chats
.
open
(
jid
);
}
_converse
.
router
.
route
(
'
converse/chat?jid=:jid
'
,
openChat
);
/************************ END Route Handlers ************************/
/************************ BEGIN Event Handlers ************************/
api
.
listen
.
on
(
'
chatBoxesFetched
'
,
autoJoinChats
);
api
.
listen
.
on
(
'
presencesInitialized
'
,
registerMessageHandlers
);
api
.
listen
.
on
(
'
clearSession
'
,
async
()
=>
{
if
(
_converse
.
shouldClearCache
())
{
await
Promise
.
all
(
_converse
.
chatboxes
.
map
(
c
=>
c
.
messages
&&
c
.
messages
.
clearStore
({
'
silent
'
:
true
}))
);
const
filter
=
o
=>
o
.
get
(
'
type
'
)
!==
_converse
.
CONTROLBOX_TYPE
;
_converse
.
chatboxes
.
clearStore
({
'
silent
'
:
true
},
filter
);
}
});
/************************ END Event Handlers ************************/
}
});
src/headless/plugins/chat/message.js
0 → 100644
View file @
e3ebde97
import
ModelWithContact
from
'
./model-with-contact.js
'
;
import
log
from
'
../../log.js
'
;
import
{
_converse
,
api
,
converse
}
from
'
../../core.js
'
;
const
u
=
converse
.
env
.
utils
;
const
{
Strophe
}
=
converse
.
env
;
/**
* Mixin which turns a `ModelWithContact` model into a non-MUC message. These can be either `chat` messages or `headline` messages.
* @mixin
* @namespace _converse.Message
* @memberOf _converse
* @example const msg = new _converse.Message({'message': 'hello world!'});
*/
const
MessageMixin
=
{
defaults
()
{
return
{
'
msgid
'
:
u
.
getUniqueId
(),
'
time
'
:
new
Date
().
toISOString
(),
'
is_ephemeral
'
:
false
};
},
async
initialize
()
{
if
(
!
this
.
checkValidity
())
{
return
;
}
this
.
initialized
=
u
.
getResolveablePromise
();
if
(
this
.
get
(
'
type
'
)
===
'
chat
'
)
{
ModelWithContact
.
prototype
.
initialize
.
apply
(
this
,
arguments
);
this
.
setRosterContact
(
Strophe
.
getBareJidFromJid
(
this
.
get
(
'
from
'
)));
}
if
(
this
.
get
(
'
file
'
))
{
this
.
on
(
'
change:put
'
,
this
.
uploadFile
,
this
);
}
this
.
setTimerForEphemeralMessage
();
/**
* Triggered once a {@link _converse.Message} has been created and initialized.
* @event _converse#messageInitialized
* @type { _converse.Message}
* @example _converse.api.listen.on('messageInitialized', model => { ... });
*/
await
api
.
trigger
(
'
messageInitialized
'
,
this
,
{
'
Synchronous
'
:
true
});
this
.
initialized
.
resolve
();
},
/**
* Sets an auto-destruct timer for this message, if it's is_ephemeral.
* @private
* @method _converse.Message#setTimerForEphemeralMessage
* @returns { Boolean } - Indicates whether the message is
* ephemeral or not, and therefore whether the timer was set or not.
*/
setTimerForEphemeralMessage
()
{
const
setTimer
=
()
=>
{
this
.
ephemeral_timer
=
window
.
setTimeout
(
this
.
safeDestroy
.
bind
(
this
),
10000
);
};
if
(
this
.
isEphemeral
())
{
setTimer
();
return
true
;
}
else
{
this
.
on
(
'
change:is_ephemeral
'
,
()
=>
this
.
isEphemeral
()
?
setTimer
()
:
clearTimeout
(
this
.
ephemeral_timer
)
);
return
false
;
}
},
checkValidity
()
{
if
(
Object
.
keys
(
this
.
attributes
).
length
===
3
)
{
// XXX: This is an empty message with only the 3 default values.
// This seems to happen when saving a newly created message
// fails for some reason.
// TODO: This is likely fixable by setting `wait` when
// creating messages. See the wait-for-messages branch.
this
.
validationError
=
'
Empty message
'
;
this
.
safeDestroy
();
return
false
;
}
return
true
;
},
/**
* Determines whether this messsage may be retracted by the current user.
* @private
* @method _converse.Messages#mayBeRetracted
* @returns { Boolean }
*/
mayBeRetracted
()
{
const
is_own_message
=
this
.
get
(
'
sender
'
)
===
'
me
'
;
return
is_own_message
&&
[
'
all
'
,
'
own
'
].
includes
(
api
.
settings
.
get
(
'
allow_message_retraction
'
));
},
safeDestroy
()
{
try
{
this
.
destroy
();
}
catch
(
e
)
{
log
.
error
(
e
);
}
},
isEphemeral
()
{
return
this
.
get
(
'
is_ephemeral
'
);
},
getDisplayName
()
{
if
(
this
.
get
(
'
type
'
)
===
'
groupchat
'
)
{
return
this
.
get
(
'
nick
'
);
}
else
if
(
this
.
contact
)
{
return
this
.
contact
.
getDisplayName
();
}
else
if
(
this
.
vcard
)
{
return
this
.
vcard
.
getDisplayName
();
}
else
{
return
this
.
get
(
'
from
'
);
}
},
getMessageText
()
{
const
{
__
}
=
_converse
;
if
(
this
.
get
(
'
is_encrypted
'
))
{
return
this
.
get
(
'
plaintext
'
)
||
this
.
get
(
'
body
'
)
||
__
(
'
Undecryptable OMEMO message
'
);
}
return
this
.
get
(
'
message
'
);
},
isMeCommand
()
{
const
text
=
this
.
getMessageText
();
if
(
!
text
)
{
return
false
;
}
return
text
.
startsWith
(
'
/me
'
);
},
/**
* Send out an IQ stanza to request a file upload slot.
* https://xmpp.org/extensions/xep-0363.html#request
* @private
* @method _converse.Message#sendSlotRequestStanza
*/
sendSlotRequestStanza
()
{
if
(
!
this
.
file
)
{
return
Promise
.
reject
(
new
Error
(
'
file is undefined
'
));
}
const
iq
=
converse
.
env
.
$iq
({
'
from
'
:
_converse
.
jid
,
'
to
'
:
this
.
get
(
'
slot_request_url
'
),
'
type
'
:
'
get
'
})
.
c
(
'
request
'
,
{
'
xmlns
'
:
Strophe
.
NS
.
HTTPUPLOAD
,
'
filename
'
:
this
.
file
.
name
,
'
size
'
:
this
.
file
.
size
,
'
content-type
'
:
this
.
file
.
type
});
return
api
.
sendIQ
(
iq
);
},
async
getRequestSlotURL
()
{
const
{
__
}
=
_converse
;
let
stanza
;
try
{
stanza
=
await
this
.
sendSlotRequestStanza
();
}
catch
(
e
)
{
log
.
error
(
e
);
return
this
.
save
({
'
type
'
:
'
error
'
,
'
message
'
:
__
(
'
Sorry, could not determine upload URL.
'
),
'
is_ephemeral
'
:
true
});
}
const
slot
=
stanza
.
querySelector
(
'
slot
'
);
if
(
slot
)
{
this
.
save
({
'
get
'
:
slot
.
querySelector
(
'
get
'
).
getAttribute
(
'
url
'
),
'
put
'
:
slot
.
querySelector
(
'
put
'
).
getAttribute
(
'
url
'
)
});
}
else
{
return
this
.
save
({
'
type
'
:
'
error
'
,
'
message
'
:
__
(
'
Sorry, could not determine file upload URL.
'
),
'
is_ephemeral
'
:
true
});
}
},
uploadFile
()
{
const
xhr
=
new
XMLHttpRequest
();
xhr
.
onreadystatechange
=
()
=>
{
if
(
xhr
.
readyState
===
XMLHttpRequest
.
DONE
)
{
log
.
info
(
'
Status:
'
+
xhr
.
status
);
if
(
xhr
.
status
===
200
||
xhr
.
status
===
201
)
{
this
.
save
({
'
upload
'
:
_converse
.
SUCCESS
,
'
oob_url
'
:
this
.
get
(
'
get
'
),
'
message
'
:
this
.
get
(
'
get
'
)
});
}
else
{
xhr
.
onerror
();
}
}
};
xhr
.
upload
.
addEventListener
(
'
progress
'
,
evt
=>
{
if
(
evt
.
lengthComputable
)
{
this
.
set
(
'
progress
'
,
evt
.
loaded
/
evt
.
total
);
}
},
false
);
xhr
.
onerror
=
()
=>
{
const
{
__
}
=
_converse
;
let
message
;
if
(
xhr
.
responseText
)
{
message
=
__
(
'
Sorry, could not succesfully upload your file. Your server’s response: "%1$s"
'
,
xhr
.
responseText
);
}
else
{
message
=
__
(
'
Sorry, could not succesfully upload your file.
'
);
}
this
.
save
({
'
type
'
:
'
error
'
,
'
upload
'
:
_converse
.
FAILURE
,
'
message
'
:
message
,
'
is_ephemeral
'
:
true
});
};
xhr
.
open
(
'
PUT
'
,
this
.
get
(
'
put
'
),
true
);
xhr
.
setRequestHeader
(
'
Content-type
'
,
this
.
file
.
type
);
xhr
.
send
(
this
.
file
);
}
};
export
default
MessageMixin
;
src/headless/plugins/chat/model-with-contact.js
0 → 100644
View file @
e3ebde97
import
{
converse
}
from
"
../../core.js
"
;
import
{
Model
}
from
'
@converse/skeletor/src/model.js
'
;
const
u
=
converse
.
env
.
utils
;
const
ModelWithContact
=
Model
.
extend
({
initialize
()
{
this
.
rosterContactAdded
=
u
.
getResolveablePromise
();
},
async
setRosterContact
(
jid
)
{
const
contact
=
await
api
.
contacts
.
get
(
jid
);
if
(
contact
)
{
this
.
contact
=
contact
;
this
.
set
(
'
nickname
'
,
contact
.
get
(
'
nickname
'
));
this
.
rosterContactAdded
.
resolve
();
}
}
});
export
default
ModelWithContact
;
src/headless/plugins/chat/model.js
0 → 100644
View file @
e3ebde97
This diff is collapsed.
Click to expand it.
src/headless/plugins/muc.js
View file @
e3ebde97
...
...
@@ -4,7 +4,7 @@
* @license Mozilla Public License (MPLv2)
* @description Implements the non-view logic for XEP-0045 Multi-User Chat
*/
import
"
./chat
"
;
import
"
./chat
/index.js
"
;
import
"
./disco
"
;
import
"
./emoji/index.js
"
;
import
{
Collection
}
from
"
@converse/skeletor/src/collection
"
;
...
...
@@ -398,7 +398,6 @@ converse.plugins.add('converse-muc', {
* @memberOf _converse
*/
_converse
.
ChatRoom
=
_converse
.
ChatBox
.
extend
({
messagesCollection
:
_converse
.
ChatRoomMessages
,
defaults
()
{
return
{
...
...
@@ -595,6 +594,10 @@ converse.plugins.add('converse-muc', {
this
.
announceReconnection
();
},
getMessagesCollection
()
{
return
new
_converse
.
ChatRoomMessages
();
},
restoreSession
()
{
const
id
=
`muc.session-
${
_converse
.
bare_jid
}
-
${
this
.
get
(
'
jid
'
)}
`
;
this
.
session
=
new
MUCSession
({
id
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment