May 2022
This has has now been converted into a theme component by @keegan
The Discord Widget does its job, but it’s really cumbersome and generally it’s an eyesore with the style of Discourse.
In the past few months I had tried to implement it everywhere (after-header, body, footer) but every time I found it too big and too customized to be permanently seen.
That’s why, in the end, I decided that a dropdown button would be the best solution. Users can decide if open the widget (maybe only to see the users online) and close it, or click the Connect button to enter the chat.
Desktop View
Display the button for ALL users (logged in and visitors):
Add this script under /admin/customize/themes
inside Desktop/Head
tab
<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
api.createWidget('discord-chat-menu', {
tagName: 'div.discord-panel',
html() {
return this.attach('menu-panel', {
contents: () => h('iframe', {
"src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
"sandbox": "allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts",
"width": "350",
"height": "500",
"allowtransparency": "true",
"frameborder": "0",
"id": "chatwidget",
"name": "chatwidget",}
)
});
},
clickOutside() {
this.sendWidgetAction('toggleDiscordChat');
}
});
api.decorateWidget('header-icons:before', function(helper) {
const headerState = helper.widget.parentWidget.state;
return helper.attach('header-dropdown', {
title: 'Discord Chat',
icon: 'fab-discord',
active: headerState.discordChatVisible,
action: 'toggleDiscordChat',
});
});
api.decorateWidget('header-icons:after', function(helper) {
const headerState = helper.widget.parentWidget.state;
if (headerState.discordChatVisible) {
return [helper.attach('discord-chat-menu')];
}
});
api.attachWidgetAction('header', 'toggleDiscordChat', function() {
this.state.discordChatVisible = !this.state.discordChatVisible;
});
</script>
Make sure to change the Url here:
"src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
entering your Server ID in place ofyour-widget-ID
. You can find it on Discord in Server Setting > Widget > Server ID.
E.g. "src": 'https://discordapp.com/widget?id=1234567890123&theme=light',
-
Inside the Url you can also replace the theme style to make it light (
&theme=light
) or dark (&theme=dark
). -
Change
"title": "Discord Chat"
as you prefer (e.g."title": "my wonderful chat"
)
Add this script under /admin/customize/themes
inside Desktop/Body
tab to reload and update the iframe every 1.5 mins (made by @Yuun, see here).
Remember to change https://discordapp.com/widget?id=your-widget-ID&theme=light
with your Url (e.g. https://discordapp.com/widget?id=1234567890123&theme=light
)
<script>
function reloadIFrame() {
var disc = document.getElementById("chatwidget");
if (disc) {
disc.src="https://discordapp.com/widget?id=your-widget-ID&theme=light";
}
}
window.setInterval("reloadIFrame();", 90000);
</script>
Display the button only to logged in users
<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
const user = require('discourse/models/user').default;
if (user !== null) {
api.createWidget('discord-chat-menu', {
tagName: 'div.discord-panel',
html() {
return this.attach('menu-panel', {
contents: () => h('iframe', {
"src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
"width": "350",
"height": "500",
"allowtransparency": "true",
"frameborder": "0",
"id": "chatwidget",
"name": "chatwidget",}
)
});
},
clickOutside() {
this.sendWidgetAction('toggleDiscordChat');
}
});
api.decorateWidget('header-icons:before', function(helper) {
const headerState = helper.widget.parentWidget.state;
return helper.attach('header-dropdown', {
title: 'Discord Chat',
icon: 'fab-discord',
active: headerState.discordChatVisible,
action: 'toggleDiscordChat',
});
});
api.decorateWidget('header-icons:after', function(helper) {
const headerState = helper.widget.parentWidget.state;
if (headerState.discordChatVisible) {
return [helper.attach('discord-chat-menu')];
}
});
api.attachWidgetAction('header', 'toggleDiscordChat', function() {
this.state.discordChatVisible = !this.state.discordChatVisible;
})};
</script>
Display the button only for users who belong to a certain trust level (and higher or lower - optional)
Change the script in this way:
<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
var level = User.currentProp("trust_level");
if (level >=2) { //Change the trust level here
api.createWidget('discord-chat-menu', {
tagName: 'div.discord-panel',
html() {
return this.attach('menu-panel', {
contents: () => h('iframe', {
"src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
"width": "350",
"height": "500",
"allowtransparency": "true",
"frameborder": "0",
"id": "chatwidget",
"name": "chatwidget",}
)
});
},
clickOutside() {
this.sendWidgetAction('toggleDiscordChat');
}
});
api.decorateWidget('header-icons:before', function(helper) {
const headerState = helper.widget.parentWidget.state;
return helper.attach('header-dropdown', {
title: 'Discord Chat',
icon: 'fab-discord',
active: headerState.discordChatVisible,
action: 'toggleDiscordChat',
});
});
api.decorateWidget('header-icons:after', function(helper) {
const headerState = helper.widget.parentWidget.state;
if (headerState.discordChatVisible) {
return [helper.attach('discord-chat-menu')];
}
});
api.attachWidgetAction('header', 'toggleDiscordChat', function() {
this.state.discordChatVisible = !this.state.discordChatVisible;
})};
</script>
In this case, the button is displayed to all users that belong to trust level 2 or higher (>=2
). Use only =2
to display it only for users at trust level 2 (no higher), or (<=2
) for users that belong to trust level 2 or lower. You can change the trust level (from 0 to 4) as needed. For more information about trust levels read Understanding Discourse Trust Levels
Display the button only for users who belong to staff (admins + mods) [@Neuferkar]
<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
const user = User.currentProp();
if (user !== null && user.staff) {
api.createWidget('discord-chat-menu', {
tagName: 'div.discord-panel',
html() {
return this.attach('menu-panel', {
contents: () => h('iframe', {
"src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
"width": "350",
"height": "500",
"allowtransparency": "true",
"frameborder": "0",
"id": "chatwidget",
"name": "chatwidget",}
)
});
},
clickOutside() {
this.sendWidgetAction('toggleDiscordChat');
}
});
api.decorateWidget('header-icons:before', function(helper) {
const headerState = helper.widget.parentWidget.state;
return helper.attach('header-dropdown', {
title: 'Discord Chat',
icon: 'fab-discord',
active: headerState.discordChatVisible,
action: 'toggleDiscordChat',
});
});
api.decorateWidget('header-icons:after', function(helper) {
const headerState = helper.widget.parentWidget.state;
if (headerState.discordChatVisible) {
return [helper.attach('discord-chat-menu')];
}
});
api.attachWidgetAction('header', 'toggleDiscordChat', function() {
this.state.discordChatVisible = !this.state.discordChatVisible;
})};
</script>
Change this line if (user !== null && user.staff)
as you wish to display the button only to admins or only to mods:
if (user !== null && user.administrator)
if (user !== null && user.moderator)
Display the button only to members of a primary group (e.g. “footeam”) [@Neuferkar and @cpradio]
Be sure to target the primary_group_name
correctly (see here)
<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
const user = User.currentProp();
if (user !== null && user.primary_group_name === "footeam") {
api.createWidget('discord-chat-menu', {
tagName: 'div.discord-panel',
html() {
return this.attach('menu-panel', {
contents: () => h('iframe', {
"src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
"width": "350",
"height": "500",
"allowtransparency": "true",
"frameborder": "0",
"id": "chatwidget",
"name": "chatwidget",}
)
});
},
clickOutside() {
this.sendWidgetAction('toggleDiscordChat');
}
});
api.decorateWidget('header-icons:before', function(helper) {
const headerState = helper.widget.parentWidget.state;
return helper.attach('header-dropdown', {
title: 'Discord Chat',
icon: 'fab-discord',
active: headerState.discordChatVisible,
action: 'toggleDiscordChat',
});
});
api.decorateWidget('header-icons:after', function(helper) {
const headerState = helper.widget.parentWidget.state;
if (headerState.discordChatVisible) {
return [helper.attach('discord-chat-menu')];
}
});
api.attachWidgetAction('header', 'toggleDiscordChat', function() {
this.state.discordChatVisible = !this.state.discordChatVisible;
})};
</script>
Mobile View
For mobile I use an icon with an invite Url to my server since on phones there is a cool Discord App to install.
Just add to your site the Custom Header Links (icons) theme component and enter in the theme settings:
- Header links:
Mobile-only link,fab-discord,https://discord.gg/INVITE_ID,vmo,blank
- Svg icons:
fab-discord
Remember to generate an invite of infinite duration, so it will always be valid and you will not have to change it again and make sure to update the Url from https://discord.gg/INVITE_ID
to your real invite ID: