Skip to content

Commit

Permalink
Merge pull request #242 from 67P/feature/241-direct_messages
Browse files Browse the repository at this point in the history
(Re)add direct messages for IRC and XMPP
  • Loading branch information
raucao committed Aug 30, 2021
2 parents b36cbdf + 66f710a commit 13db2a5
Show file tree
Hide file tree
Showing 21 changed files with 206 additions and 73 deletions.
12 changes: 9 additions & 3 deletions app/components/channel-nav/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@
{{#each subspace.channels as |channel|}}
<li class="channel group relative {{if channel.connected "connected" "disconnected"}} {{channel.unreadMessagesClass}} {{if channel.visible "active bg-white bg-opacity-20" ""}}">
{{#if channel.isUserChannel}}
<LinkTo @route="user-channel" @model={{channel}} class="hc-sidebar-item">
{{channel.name}}
</LinkTo>
{{#if (eq channel.protocol "XMPP")}}
<LinkTo @route="user-channel" @model={{channel}} class="hc-sidebar-item">
<IconCornerDownRight @class="ml-2" />&thinsp;{{channel.displayName}}
</LinkTo>
{{else}}
<LinkTo @route="user-channel" @model={{channel}} class="hc-sidebar-item">
{{channel.displayName}}
</LinkTo>
{{/if}}
{{else}}
<LinkTo @route="channel" @model={{channel}} class="hc-sidebar-item">
#&thinsp;{{channel.shortName}}
Expand Down
13 changes: 13 additions & 0 deletions app/components/link-to-username/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,17 @@ export default class LinkToUsernameComponent extends Component {
return this.args.username.replace(regex, '');
}

get userChannelId () {
let id;
switch (this.args.channel.protocol) {
case 'IRC':
id = `${this.usernameWithoutPrefix}@${this.args.channel.domain}`;
break;
case 'XMPP':
id = `${this.args.channel.id}${encodeURIComponent('/')}${this.usernameWithoutPrefix}`;
break;
}
return id;
}

}
2 changes: 1 addition & 1 deletion app/components/link-to-username/template.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<LinkTo @route="user-channel"
@model={{this.usernameWithoutPrefix}}
@model={{this.userChannelId}}
class="hc-sidebar-item {{this.role}}">
{{@username}}
</LinkTo>
7 changes: 1 addition & 6 deletions app/components/user-list/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@
{{/if}}
<ul>
{{#each this.renderedUsers as |username|}}
{{#if @allowUserChannels}}
<li><LinkToUsername @username={{username}} /></li>
{{else}}
<li><a class="hc-sidebar-item">{{username}}</a></li>
{{/if}}
<li><LinkToUsername @username={{username}} @channel={{@channel}}/></li>
{{/each}}

<li class="last-element"></li>
<ScrollingObserver @rootElement="#user-list"
@rootMargin="200px"
Expand Down
6 changes: 1 addition & 5 deletions app/controllers/channel/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import Controller, { inject as controller } from '@ember/controller';

export default class ChannelIndexController extends Controller {

@controller('channel') channel;

get allowUserChannels () {
// FIXME allow when fixed for IRC
// TODO implement channel DMs for XMPP
return false;
}
}
3 changes: 2 additions & 1 deletion app/models/base_channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ export default class BaseChannel {
}
get domain () {
return this.id.match(/@(.+)$/)[1];
const match = this.id.match(/@([^/]+)/);
return match[1];
}
get unreadMessagesClass () {
Expand Down
12 changes: 8 additions & 4 deletions app/routes/base_channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ export default class BaseChannelRoute extends Route {
@alias('localData.stores.userSettings') userSettings;

model (params) {
const channel = this.coms.channels.findBy('slug', params.slug);
let channel = this.coms.channels.findBy('slug', params.slug);
if (channel) return channel;

if (channel) {
const channelId = decodeURIComponent(params.slug);
const domain = channelId.match(/@([^/]+)/)[1];
const randomChannelForDomain = this.coms.channels.findBy('domain', domain);

if (randomChannelForDomain) {
channel = this.createChannelOrUserChannel(randomChannelForDomain.account, channelId);
return channel;
// TODO must select acccount automatically (for IRC)
// channel = this.createChannelOrUserChannel(account, params.slug);
} else {
const firstChannel = this.coms.channels.firstObject;
this.router.transitionTo('channel', firstChannel);
Expand Down
15 changes: 11 additions & 4 deletions app/routes/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ import BaseChannel from 'hyperchannel/routes/base_channel';

export default class ChannelRoute extends BaseChannel {

// FIXME must select acccount automatically (for IRC)
createChannelOrUserChannel (account, channelName) {
// TODO this is IRC only
return this.coms.createChannel(account, "#" + channelName);
createChannelOrUserChannel (account, channelId) {
let channel;
switch(account.protocol) {
case 'IRC':
channel = this.coms.createChannel(account, '#'+channelId.match(/^(.+)@/)[1]);
break;
case 'XMPP':
channel = this.coms.createChannel(account, channelId);
break;
}
return channel;
}

}
14 changes: 11 additions & 3 deletions app/routes/user_channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ import BaseChannel from 'hyperchannel/routes/base_channel';

export default BaseChannel.extend({

// FIXME must select acccount automatically
createChannelOrUserChannel: function(account, channelName){
return this.coms.createUserChannel(account, channelName);
createChannelOrUserChannel: function(account, channelId) {
let channel;
switch(account.protocol) {
case 'IRC':
channel = this.coms.createUserChannel(account, channelId.match(/^(.+)@/)[1]);
break;
case 'XMPP':
channel = this.coms.createUserChannel(account, channelId);
break;
}
return channel;
}

});
31 changes: 15 additions & 16 deletions app/services/coms.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import Service, { inject as service } from '@ember/service';
import { isPresent, isEmpty } from '@ember/utils';
import { A } from '@ember/array';
import XmppAccount from 'hyperchannel/models/account/xmpp';
import IrcAccount from 'hyperchannel/models/account/irc';
import Channel from 'hyperchannel/models/channel';
import UserChannel from 'hyperchannel/models/user_channel';
import Message from 'hyperchannel/models/message';
import config from 'hyperchannel/config/environment';
import moment from 'moment';
import { tracked } from '@glimmer/tracking';
import { computed } from '@ember/object';
import { sort } from '@ember/object/computed';
import IrcAccount from 'hyperchannel/models/account/irc';
import XmppAccount from 'hyperchannel/models/account/xmpp';
import Channel from 'hyperchannel/models/channel';
import Message from 'hyperchannel/models/message';
import config from 'hyperchannel/config/environment';

/**
* This service provides the central command interface for communicating with
Expand All @@ -32,6 +31,10 @@ export default class ComsService extends Service {
* @type {Account[]}
*/
@tracked accounts = A([]);
/**
* A collection of all channel instances
* @type {Channel[] | UserChannel}
*/
@tracked channels = A([]);

channelSorting = ['name'];
Expand Down Expand Up @@ -346,24 +349,20 @@ export default class ComsService extends Service {
});
}

createUserChannel (account, userName) {
const channel = new UserChannel({
account: account,
name: userName
});
createUserChannel (account, name) {
const channel = this.getServiceForSockethubPlatform(account.protocol)
.createUserChannel(account, name);

// TODO check if this is necesarry for XMPP,
// because for IRC it is not
this.joinChannel(channel, "person");
this.channels.pushObject(channel);

return channel;
}

async removeChannel (channel) {
this.leaveChannel(channel);
this.channels.removeObject(channel);
await this.storage.removeChannel(channel);
if (!channel.isUserChannel) {
await this.storage.removeChannel(channel);
}
return;
}

Expand Down
19 changes: 19 additions & 0 deletions app/services/sockethub-irc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Service, { inject as service } from '@ember/service';
import { isEmpty } from '@ember/utils';
import UserChannel from 'hyperchannel/models/user_channel';
import channelMessageFromSockethubObject from 'hyperchannel/utils/channel-message-from-sockethub-object';
import extend from 'extend';

Expand Down Expand Up @@ -278,6 +279,24 @@ export default class SockethubIrcService extends Service {
return channel;
}

/**
* Create a direct-message channel
*
* @param {Account} account
* @param {String} nickname
* @returns {UserChannel} user channel
* @public
*/
createUserChannel (account, nickname) {
const channel = new UserChannel({
account: account,
name: nickname,
displayName: nickname,
connected: true
});
return channel;
}

/**
* Utility function for easier logging
* @protected
Expand Down
43 changes: 32 additions & 11 deletions app/services/sockethub-xmpp.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Service, { inject as service } from '@ember/service';
import { isEmpty } from '@ember/utils';
import extend from 'extend';
import UserChannel from 'hyperchannel/models/user_channel';
import channelMessageFromSockethubObject from 'hyperchannel/utils/channel-message-from-sockethub-object';

/**
Expand Down Expand Up @@ -186,7 +187,7 @@ export default class SockethubXmppService extends Service {
addMessageToChannel (message) {
if (isEmpty(message.object.content)) return;

const channel = this.getChannelForMessage(message);
const channel = this.findOrCreateChannelForMessage(message);
const channelMessage = channelMessageFromSockethubObject(message);

// TODO should check for message and update sent status if exists
Expand Down Expand Up @@ -221,29 +222,49 @@ export default class SockethubXmppService extends Service {
* @returns {Channel} channel
* @public
*/
getChannelForMessage (message) {
findOrCreateChannelForMessage (message) {
const targetChannelId = message.target['@id'];
let channel;

if (message.target['@type'] === 'room') {
channel = this.coms.channels.findBy('sockethubChannelId', targetChannelId);

// TODO Find account for new channel by sockerhubPersonId
console.warn('Received message for unknown channel', message);
// if (!channel) {
// channel = this.coms.createChannel(space, targetChannelId);
// }
if (!channel) {
console.warn('Received message for unknown channel', message);
// channel = this.coms.createChannel(space, targetChannelId);
}
} else {
channel = this.coms.channels.findBy('sockethubChannelId', message.actor['@id']);
// TODO
console.warn('Received message for unknown user channel', message);
// if (!channel) {
// channel = this.coms.createUserChannel(space, message.actor['@id']);
// }

if (!channel) {
const account = this.coms.accounts.findBy('sockethubPersonId', message.target['@id']);
if (!account) console.warn('Received direct message for unknown account', message);
channel = this.coms.createUserChannel(account, message.actor['@id']);
}
}

return channel;
}

/**
* Create a direct-message channel
*
* @param {Account} account
* @param {String} sockethub actor ID
* @returns {UserChannel} user channel
* @public
*/
createUserChannel (account, sockethubActorId) {
const channel = new UserChannel({
account: account,
name: sockethubActorId, // e.g. kosmos-dev@kosmos.chat/jimmy
displayName: sockethubActorId.match(/\/(.+)$/)[1],
connected: true
});
return channel;
}

/**
* Utility function for easier logging
* @private
Expand Down
4 changes: 4 additions & 0 deletions app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ header {
@apply block px-4 h-7 leading-7
overflow-hidden overflow-ellipsis
hover:bg-white hover:bg-opacity-20;

svg {
@apply inline-block h-4 w-4 -mt-0.5;
}
}
}

Expand Down
3 changes: 1 addition & 2 deletions app/templates/channel/index.hbs
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
<UserList @users={{this.model.sortedUserList}}
@allowUserChannels={{this.allowUserChannels}} />
<UserList @users={{this.model.sortedUserList}} @channel={{this.model}} />
2 changes: 1 addition & 1 deletion app/templates/components/icon-corner-down-right.hbs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-corner-down-right"><polyline points="15 10 20 15 15 20"></polyline><path d="M4 4v7a4 4 0 0 0 4 4h12"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-corner-down-right {{@class}}"><polyline points="15 10 20 15 15 20"></polyline><path d="M4 4v7a4 4 0 0 0 4 4h12"></path></svg>
14 changes: 11 additions & 3 deletions app/utils/channel-message-from-sockethub-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,24 @@ import { isEmpty } from '@ember/utils';
import Message from 'hyperchannel/models/message';

export default function channelMessageFromSockethubObject(message) {
let channelMessage = new Message({
const channelMessage = new Message({
type: message.object['@type'] === 'me' ? 'message-chat-me' : 'message-chat',
date: extractDate(message.published),
nickname: message.actor.displayName || message.actor['@id'],
nickname: extractNickname(message.actor),
content: message.object.content
});

return channelMessage;
}

function extractNickname (actor) {
if (actor.displayName) {
return actor.displayName;
} else {
const matchChannelUser = actor['@id'].match(/^.+@.+\/(.*)$/);
return matchChannelUser ? matchChannelUser[1] : actor['@id'];
}
}

function extractDate(dateField) {
if (isEmpty(dateField)) {
return new Date();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// import { module, test } from 'qunit';
// import { setupRenderingTest } from 'ember-qunit';
// import { render } from '@ember/test-helpers';
// import hbs from 'htmlbars-inline-precompile';
// import { hbs } from 'ember-cli-htmlbars';
//
// module('Integration | Component | channel-nav', function(hooks) {
// setupRenderingTest(hooks);
//
// test('it renders', async function(assert) {
// assert.expect(1);
// // Set any properties with this.set('myProperty', 'value');
// // Handle any actions with this.set('myAction', function(val) { ... });
//
// await render(hbs`{{channel-nav}}`);
// await render(hbs`<ChannelNav />`);
//
// assert.dom(this.element).hasText('');
// assert.equal(this.element.textContent.trim(), '');
// });
// });
//
Loading

0 comments on commit 13db2a5

Please sign in to comment.