Channel + User Mentions
This commit is contained in:
parent
a5b7628768
commit
cd38bec601
@ -1,8 +1,10 @@
|
||||
# Accord - Like Discord but cooler.
|
||||
|
||||
Custom Frontend and Backend that is similar to Discord.
|
||||
> Built with React, Redux, and Node.js.
|
||||
|
||||
> Built with React TS, Redux, and Node.js.
|
||||
Accord is an app, similar to Discord, but cooler.
|
||||
|
||||
**Please star this repo to support development.**
|
||||
|
||||
<a href="https://ibb.co/kgndDwd"><img src="https://i.ibb.co/N6h4NJ4/Screenshot-from-2021-08-31-16-09-41.png" alt="Screenshot-from-2021-08-31-16-09-41" border="0" /></a>
|
||||
<a href="https://ibb.co/st2q2B0"><img src="https://i.ibb.co/fQ2H2ch/Screenshot-from-2021-08-30-11-55-01.png" alt="Screenshot-from-2021-08-30-11-55-01" border="0" /></a>
|
||||
|
@ -53,10 +53,7 @@ const MessageContent: FunctionComponent<MessageContentProps> = ({ message }) =>
|
||||
|
||||
const messageHTML =
|
||||
((message.content)
|
||||
? format(mentions
|
||||
.tagsToHTML(
|
||||
striptags(mentions
|
||||
.stripFormat(message.content), mentions.tags)))
|
||||
? format(mentions.tagsToHTML(striptags(mentions.stripFormat(message.content), mentions.tags)))
|
||||
: ''
|
||||
) + ((message.updatedAt && message.content)
|
||||
? `<span
|
||||
|
@ -12,6 +12,7 @@ describe('mention-service', () => {
|
||||
},
|
||||
entities: {
|
||||
channels: [{ id: '246688207138279430', name: 'general', guildId: '246688207148279429' }],
|
||||
roles: [{ id: '246688207138279435', name: '@everyone', guildId: '246688207148279429' }],
|
||||
users: [{ id: '246688207138279428', username: 'Adam', discriminator: 1 }],
|
||||
}
|
||||
} as any;
|
||||
@ -21,11 +22,17 @@ describe('mention-service', () => {
|
||||
};
|
||||
|
||||
test(fn('formatOriginal'), () => {
|
||||
given('@Adam#0001').expect('<@246688207138279428>');
|
||||
given('@nobody#0000').expect('@nobody#0000');
|
||||
given('hi @Adam#0001').expect('hi <@246688207138279428>');
|
||||
given('@Adam#0001 @Adam#0001').expect('<@246688207138279428> <@246688207138279428>');
|
||||
given('@Adam#0001@Adam#0001').expect('<@246688207138279428><@246688207138279428>');
|
||||
given('#general').expect('<#246688207138279430>');
|
||||
given('#non-existent-channel').expect('#non-existent-channel');
|
||||
given('let\'s talk in #general').expect('let\'s talk in <#246688207138279430>');
|
||||
given('#general #general').expect('<#246688207138279430> <#246688207138279430>');
|
||||
given('#general#general').expect('<#246688207138279430><#246688207138279430>');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
export {}
|
@ -1,7 +1,6 @@
|
||||
import { getChannel, getChannelByName } from '../store/channels';
|
||||
import { getGuildUsers } from '../store/guilds';
|
||||
import { getRole, getRoleByName } from '../store/roles';
|
||||
import { getUser, getUserByTag } from '../store/users';
|
||||
import patterns from '../types/patterns';
|
||||
|
||||
export class MentionService {
|
||||
public readonly tags = ['mention'];
|
||||
@ -14,57 +13,55 @@ export class MentionService {
|
||||
private readonly patterns = {
|
||||
formatted: {
|
||||
channel: /<#(\d{18})>/gm,
|
||||
role: /<&(\d{18})>/gm,
|
||||
user: /<@(\d{18})>/gm,
|
||||
},
|
||||
original: {
|
||||
channel: /#([A-Za-z\-\d]{2,32})/gm,
|
||||
role: /@((.*){2,32})/gm,
|
||||
user: /@([A-Za-z\d\-\_ ]{2,32}#\d{4})/gm,
|
||||
},
|
||||
tag: /<mention type="(user|everyone|someone|here)" id="(\d{18})" \/>/,
|
||||
tag: /<mention type="(channel|role|user)" id="(\d{18})" \/>/,
|
||||
};
|
||||
|
||||
constructor(private state: Store.AppState) {}
|
||||
|
||||
// messageBox.onInput -> formatted mentions appear fancy in message box
|
||||
public formatOriginal(content: string) {
|
||||
const guildId = this.state.ui.activeGuild!.id;
|
||||
return content
|
||||
// .replace(this.patterns.original.role, (_, tag) => {
|
||||
// const user = getUserByTag(tag)(this.state);
|
||||
// return `<@${user.id}>`;
|
||||
// })
|
||||
.replace(this.patterns.original.user, (_, tag) => {
|
||||
const user = getUserByTag(tag)(this.state);
|
||||
return (user) ? `<@${user.id}>` : '';
|
||||
.replace(this.patterns.original.channel, (og, name) => {
|
||||
const channel = getChannelByName(guildId, name)(this.state);
|
||||
return (channel) ? `<#${channel?.id}>` : og;
|
||||
})
|
||||
.replace(this.patterns.original.user, (og, tag) => {
|
||||
const user = getUserByTag(tag)(this.state);
|
||||
return (user) ? `<@${user.id}>` : og;
|
||||
})
|
||||
.replace(this.patterns.original.channel, (_, name) => {
|
||||
const channel = getChannelByName(this.state.ui.activeGuild!.id, name)(this.state);
|
||||
return (channel) ? `<#${channel?.id}>` : '';
|
||||
});
|
||||
}
|
||||
// public stripFormat(content: string) {
|
||||
// return content
|
||||
// .replace(this.patterns.formatted.user, `<mention type="user" id="$1" />`)
|
||||
// }
|
||||
// public tagsToHTML(content: string) {
|
||||
// return content.replace(this.patterns., (_, type, id) => {
|
||||
// const tag = {
|
||||
// user: {
|
||||
// onClick: `events.emit('openUserProfile', '${id}')`,
|
||||
// text: `@${this.getUserTag(id)}`,
|
||||
// }
|
||||
// };
|
||||
public stripFormat(content: string) {
|
||||
return content
|
||||
.replace(this.patterns.formatted.channel, `<mention type="channel" id="$1" />`)
|
||||
.replace(this.patterns.formatted.user, `<mention type="user" id="$1" />`);
|
||||
}
|
||||
|
||||
// return `<a
|
||||
// data-id="${id}"
|
||||
// class="font-extrabold cursor-pointer"
|
||||
// onclick="${tag[type].onClick}">${tag[type].text}</a>`;
|
||||
// });
|
||||
// }
|
||||
public tagsToHTML(content: string) {
|
||||
return content.replace(this.patterns.tag, (_, type, id) => {
|
||||
const guildId = this.state.ui.activeGuild!.id;
|
||||
const tag = {
|
||||
channel: {
|
||||
onClick: `window.location.href = '/channels/${guildId}/${id}'`,
|
||||
text: `#${getChannel(id)(this.state)?.name}`,
|
||||
},
|
||||
user: {
|
||||
onClick: `events.emit('openUserProfile', '${id}')`,
|
||||
text: `@${this.tag(getUser(id)(this.state))}`,
|
||||
},
|
||||
};
|
||||
|
||||
private getUserTag(userId: string) {
|
||||
const user = getUser(userId)(this.state);
|
||||
return this.tag(user);
|
||||
return `<a
|
||||
data-id="${id}"
|
||||
class="font-extrabold cursor-pointer hover:underline"
|
||||
onclick="${tag[type].onClick}">${tag[type].text}</a>`;
|
||||
});
|
||||
}
|
||||
|
||||
private tag(user: Entity.User) {
|
||||
|
@ -33,6 +33,11 @@ export const getRole = (id: string) => createSelector<Store.AppState, Entity.Rol
|
||||
roles => roles.find(r => r.id === id),
|
||||
);
|
||||
|
||||
export const getRoleByName = (guildId: string, name: string) => createSelector<Store.AppState, Entity.Role[], Entity.Role | undefined>(
|
||||
state => state.entities.roles,
|
||||
roles => roles.find(r => r.guildId === guildId && r.name === name),
|
||||
);
|
||||
|
||||
export const getRoles = (ids: string[]) => createSelector<Store.AppState, Entity.Role[], Entity.Role[]>(
|
||||
state => state.entities.roles,
|
||||
roles => roles.filter(r => ids.includes(r.id)),
|
||||
|
@ -83,14 +83,10 @@ export const getUser = (id: string) =>
|
||||
);
|
||||
|
||||
export const getUserByTag = (tag: string) =>
|
||||
createSelector<Store.AppState, Entity.User[], Entity.User>(
|
||||
createSelector<Store.AppState, Entity.User[], Entity.User | undefined>(
|
||||
state => state.entities.users,
|
||||
users => {
|
||||
const [username, discrim] = tag.split('#');
|
||||
return users.find(u => u.username === username && u.discriminator === +discrim) ?? {
|
||||
avatarURL: '/avatars/unknown.png',
|
||||
discriminator: 0,
|
||||
username: 'Unknown',
|
||||
} as Entity.User;
|
||||
return users.find(u => u.username === username && u.discriminator === +discrim);
|
||||
}
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user