Skip to content

Commit

Permalink
(Re)add direct messages for IRC and XMPP
Browse files Browse the repository at this point in the history
Also re-adds the ability to open URLs for channels that aren't currently
joined.

For XMPP, the DM channel will be rendered under the channel it belongs
to, with an icon that indicates the scope. (We can improve channel
titles/info etc. later on.)

closes #241, #126
  • Loading branch information
raucao committed Aug 28, 2021
1 parent 64c307a commit 52dba48
Show file tree
Hide file tree
Showing 21 changed files with 197 additions and 68 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;
}

});
17 changes: 6 additions & 11 deletions app/services/coms.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ 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 UserChannel from 'hyperchannel/models/user_channel';
import Message from 'hyperchannel/models/message';
import config from 'hyperchannel/config/environment';

Expand Down Expand Up @@ -350,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 52dba48

Please sign in to comment.