Skip to content

Commit

Permalink
Online Widget
Browse files Browse the repository at this point in the history
  • Loading branch information
KyrneDev committed Aug 19, 2021
1 parent 1404bde commit 8351526
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 46 deletions.
4 changes: 2 additions & 2 deletions extend.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@

(new Frontend('forum'))
->js(__DIR__.'/js/dist/forum.js')
->css(__DIR__.'/less/forum.less'),
->css(__DIR__ . '/resources/less/forum.less'),

(new Frontend('admin'))
->js(__DIR__.'/js/dist/admin.js')
->css(__DIR__.'/less/admin.less')
->css(__DIR__ . '/resources/less/admin.less')
->content(AddStatsData::class),

new Locales(__DIR__.'/resources/locale'),
Expand Down
2 changes: 1 addition & 1 deletion js/dist/admin.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/admin.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/forum.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/dist/forum.js.map

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions js/src/admin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import DashboardPage from 'flarum/components/DashboardPage';

import WebsocketPage from './components/WebsocketPage';
import WebsocketStatsWidget from "./components/WebsocketStatsWidget";
import RegisterWidget from "../common/Widget/RegisterWidget";

app.initializers.add('kyrne-websocket', app => {

Expand All @@ -15,5 +16,9 @@ app.initializers.add('kyrne-websocket', app => {
extend(DashboardPage.prototype, 'availableWidgets', widgets => {
widgets.add('websocketstatistics', <WebsocketStatsWidget/>, 15);
});

if (app.initializers.has('afrux/forum-widgets-core')) {
RegisterWidget(app);
}
});

15 changes: 15 additions & 0 deletions js/src/common/Widget/RegisterWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Widgets from 'flarum/extensions/afrux-forum-widgets-core/common/extend/Widgets';

import WebsocketOnlineUsersWidget from './WebsocketOnlineUsersWidget';

export default function(app) {
(new Widgets).add({
key: 'WebsocketOnlineUsersWidget',
component: WebsocketOnlineUsersWidget,

isDisabled: false,

placement: 'end',
position: 1,
}).extend(app, 'kyrne-websocket');
};
127 changes: 127 additions & 0 deletions js/src/common/Widget/WebsocketOnlineUsersWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import LoadingIndicator from 'flarum/components/LoadingIndicator';
import Tooltip from 'flarum/components/Tooltip';
import avatar from 'flarum/helpers/avatar';
import Link from 'flarum/components/Link';
import stringToColor from 'flarum/utils/stringToColor';
import Stream from 'flarum/utils/Stream';

import Widget from 'flarum/extensions/afrux-forum-widgets-core/common/components/Widget';

let WebsocketWidget = null;

if (Widget) {
WebsocketWidget = class WebsocketOnlineUsersWidget extends Widget {
oninit(vnode) {
super.oninit(vnode);

this.users = [];
this.loading = true;
}

oncreate(vnode) {
super.oncreate(vnode);

app.pusher.then(object => {
const presence = object.channels.presence;

const existingMembers = Object.keys(presence.members.members);

if (existingMembers.length === 0) {

presence.bind("pusher:subscription_succeeded", members => {
Object.keys(members.members).map(member => {
if (presence.members.myID != member && !member.includes('Guest')) {
members.members[member].id = member;
this.pushMember(members.members[member]);
}
})
this.loading = false;
m.redraw();
});
} else {
existingMembers.map(member => {
if (presence.members.myID != member && !member.includes('Guest')) {
presence.members.members[member].id = member;
this.pushMember(presence.members.members[member]);
}
})
this.loading = false;
m.redraw();
}

presence.bind("pusher:member_removed", (member) => {
this.users.some((user, i) => {
if (user.id() == member.id) {
this.users.splice(i, 1);
m.redraw();
return true;
}
});
});

presence.bind("pusher:member_added", (member) => {
if (presence.members.myID != member.id && typeof member.id !== 'string') {
member.info.id = member.id;
this.pushMember(member.info);
m.redraw();
}
});
});
}

pushMember(member) {
this.users.push({
id: Stream(member.id),
color: Stream('#' + stringToColor(member.username)),
displayName: Stream(member.username),
avatarUrl: Stream(member.avatarUrl),
slug: Stream(member.slug)
});
}

className() {
return 'WebsocketOnlineUsersWidget';
}

icon() {
return 'fas fa-user-friends';
}

title() {
return app.translator.trans('kyrne-websocket.forum.widget.title');
}

content() {
if (this.loading) {
return <LoadingIndicator/>;
}

const max = 15;
const users = this.users;

return (
<div className="WebsocketOnlineUsersWidget-users">
<div className="WebsocketOnlineUsersWidget-users-message">
{users.length === 0 ? app.translator.trans('kyrne-websocket.forum.widget.empty') : null}
</div>
<div className="WebsocketOnlineUsersWidget-users-list">
{users.slice(0, max).map((user) => (
<Link href={app.route('user', {username: user.slug()})} className="WebsocketOnlineUsersWidget-users-item">
<Tooltip text={user.displayName()}>{avatar(user)}</Tooltip>
</Link>
))}
{users.length > max ? (
<span className="WebsocketOnlineUsersWidget-users-item WebsocketOnlineUsersWidget-users-item--plus">
<span className="Avatar">{`+${max}`}</span>
</span>
) : null}
</div>
</div>
);
}
}
} else {
WebsocketWidget = class WebsocketOnlineUsersWidget {};
}

export default WebsocketWidget;
40 changes: 21 additions & 19 deletions js/src/forum/PresenceChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ import stringToColor from 'flarum/utils/stringToColor';
export default function () {
extend(DiscussionPage.prototype, 'view', function (vnode) {
app.pusher.then(object => {
if (!app.discussions.presence && app.session.user && this.discussion) {
if (!app.discussions.presence && this.discussion) {
app.discussions.presence = object.pusher.subscribe('presence-' + this.discussion.id());

app.discussions.presence.bind("pusher:subscription_succeeded", (members) => {
this.membersOnline = [];
Object.keys(members.members).map(member => {
if (app.session.user.id() != member) {
if (app.discussions.presence.members.myID != member && typeof member !== 'string') {
this.membersOnline.push({
id: Stream(member),
color: Stream(stringToColor(members.members[member].username)),
color: Stream('#' + stringToColor(members.members[member].username)),
displayName: Stream(members.members[member].username),
avatarUrl: Stream(members.members[member].avatarUrl)
});
Expand All @@ -39,10 +39,10 @@ export default function () {
});

app.discussions.presence.bind("pusher:member_added", (member) => {
if (app.session.user.id() != member.id) {
if (app.discussions.presence.members.myID != member.id && typeof member.id !== 'string') {
this.membersOnline.push({
id: Stream(member.id),
color: Stream(stringToColor(member.info.username)),
color: Stream('#' + stringToColor(member.info.username)),
displayName: Stream(member.info.username),
avatarUrl: Stream(member.info.avatarUrl)
});
Expand Down Expand Up @@ -108,18 +108,20 @@ export default function () {
});

extend(ReplyComposer.prototype, 'view', function () {
$('.TextEditor-editor').on('keydown', () => {
if (this.typingTimeout) {
this.typingTimeout = false;
app.request({
method: 'POST',
url: app.forum.attribute('apiUrl') + '/posts/typing',
body: {
discussionId: this.attrs.discussion.id()
}
})
}
})
if (app.session.user) {
$('.TextEditor-editor').on('keydown', () => {
if (this.typingTimeout) {
this.typingTimeout = false;
app.request({
method: 'POST',
url: app.forum.attribute('apiUrl') + '/posts/typing',
body: {
discussionId: this.attrs.discussion.id()
}
})
}
})
}
});

extend(ReplyPlaceholder.prototype, 'oninit', function () {
Expand All @@ -128,10 +130,10 @@ export default function () {
setTimeout(() => {
if (app.discussions.presence) {
app.discussions.presence.bind('typing', (data) => {
if (!this.typers[data.userId] && data.userId != app.session.user.id()) {
if (!this.typers[data.userId] && data.userId != app.discussions.presence.members.myID) {
this.typers[data.userId] = {
id: Stream(data.userId),
color: Stream(stringToColor(data.username)),
color: Stream('#' + stringToColor(data.username)),
displayName: Stream(data.username),
avatarUrl: Stream(data.avatarUrl),
time: new Date()
Expand Down
9 changes: 8 additions & 1 deletion js/src/forum/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import DiscussionPage from 'flarum/components/DiscussionPage';
import IndexPage from 'flarum/components/IndexPage';
import Button from 'flarum/components/Button';
import Stream from 'flarum/utils/Stream';

import PresenceChannel from './PresenceChannel';
import RegisterWidget from '../common/Widget/RegisterWidget';

app.initializers.add('kyrne-websocket', () => {

Expand Down Expand Up @@ -42,7 +44,8 @@ app.initializers.add('kyrne-websocket', () => {
return resolve({
channels: {
main: socket.subscribe('public'),
user: app.session.user ? socket.subscribe('private-user' + app.session.user.id()) : null
user: app.session.user ? socket.subscribe('private-user' + app.session.user.id()) : null,
presence: socket.subscribe('presence-forum')
},
pusher: socket,
});
Expand Down Expand Up @@ -238,4 +241,8 @@ app.initializers.add('kyrne-websocket', () => {
});

PresenceChannel();

if (app.initializers.has('afrux/forum-widgets-core')) {
RegisterWidget(app);
}
});
File renamed without changes.
32 changes: 32 additions & 0 deletions less/forum.less → resources/less/forum.less
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,35 @@
.DiscussionPage-nav > ul.websocket-nav {
margin-top: -120px;
}


/*!
* Online Users Widget v0.1.1 by @SychO9
* License - MIT https://github.com/afrux/online-users-widget/blob/main/LICENSE.md
*/
.WebsocketOnlineUsersWidget-users {
&-list {
display: flex;
flex-wrap: wrap;
}

&-item {
margin: 2px;

.Avatar {
--size: 35px;
width: var(--size);
height: var(--size);
font-size: calc(~"var(--size) / 2");
display: inline-flex;
justify-content: center;
align-items: center;
}

&--plus .Avatar {
background-color: @body-bg;
color: @control-color;
font-size: 13px;
}
}
}
6 changes: 6 additions & 0 deletions resources/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ kyrne-websocket:
discussion_page:
viewing_title: "Here now:"

# These translations are used for the online users widget.
widget:
title: Online Users
empty: No Users Currently Online.


##
# UNIQUE KEYS - The following keys are used in only one location each.
##
Expand Down
Loading

0 comments on commit 8351526

Please sign in to comment.