How to display "Discord Widget" in a dropdown button


(Daniela) #1

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>Discourse._registerPluginCode('0.8.9', function (api) {
            api.decorateWidget('header-icons:before', function (helper) {
                return helper.h('li.dropdown#chat', [helper.h('button.dropdown-toggle#chat-button.icon', {
                    'attributes': {
                        "type": "button",
                        "data-toggle": "dropdown",
                        "title": "discord chat"
                    } }, helper.h('i.fa.fa-comments-o.chat-button-icon.d-icon')), helper.h('ul.dropdown-menu#chat', [helper.h('li', [helper.h('iframe', {
                    "src": 'https://discordapp.com/widget?id=YOUR_SERVER_ID&theme=light', 
                    "width": "350",
                    "height": "500",
                    "allowtransparency": "true",
                    "frameborder": "0",
                    "id": "chatwidget",
                    "name": "chatwidget"
                    
                })])])]);
            });
    });</script> 

Make sure to change the Url here:

  • "src": 'https://discordapp.com/widget?id=YOUR_SERVER_ID&theme=light', entering your Server ID in place of YOUR_SERVER_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")

  • You can use another Fontawesome icon changing this line:

    • ('i.fa.fa-comments-o.chat-button-icon.d-icon') , replacing only the comments-o part with the new icon’s name

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 "widget source url"; 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="widget source url";
        }
    }
    window.setInterval("reloadIFrame();", 90000);
</script>

Under Desktop/CSS

#chat.dropdown-menu {
    top: 40px; /*because I use a custom header probably you need to change a bit this value*/
    left: -185px;
    width: 350px;
}

#chatwidget {
    border: 1px solid #e9e9e9;
    box-shadow: 0 2px 2px rgba(0,0,0,0.25);
}   

#chat-button.icon {
    background-color: transparent;
    border-bottom: 1px solid transparent;
}

#chat-button.icon:hover {
    color: #222;
    background-color: #e9e9e9;
}

#chat.dropdown.open {
    color: #7b7b7b;
    background-color: #fff;
    border-top: 1px solid #e9e9e9;
    border-left: 1px solid #e9e9e9;
    border-right: 1px solid #e9e9e9;
}

Display the button only to logged in users

<script>Discourse._registerPluginCode('0.8.9', function (api) {
   const user = Discourse.User.current(); 
    if (user !== null) {  
        api.decorateWidget('header-icons:before', function (helper) {
            return helper.h('li.dropdown#chat', [helper.h('button.dropdown-toggle#chat-button.icon', {
                'attributes': {
                    "type": "button",
                    "data-toggle": "dropdown",
                    "title": "discord chat"
                } }, helper.h('i.fa.fa-comments-o.chat-button-icon.d-icon')), helper.h('ul.dropdown-menu#chat', [helper.h('li', [helper.h('iframe', {
                "src": 'https://discordapp.com/widget?id=YOUR_SERVER_ID&theme=light',
                "width": "350",
                "height": "500",
                "allowtransparency": "true",
                "frameborder": "0",
                "id": "chatwidget",
                "name": "chatwidget",

            })])])]);
        });
    }
});</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>Discourse._registerPluginCode('0.8.9', function (api) {
   var level = Discourse.User.currentProp("trust_level");
   if (level >=2) {  //Change the trust level here
        api.decorateWidget('header-icons:before', function (helper) {
            return helper.h('li.dropdown#chat', [helper.h('button.dropdown-toggle#chat-button.icon', {
                'attributes': {
                    "type": "button",
                    "data-toggle": "dropdown",
                    "title": "discord chat for TL2 users and higher"
                } }, helper.h('i.fa.fa-comments-o.chat-button-icon.d-icon')), helper.h('ul.dropdown-menu#chat', [helper.h('li', [helper.h('iframe', {
                "src": 'https://discordapp.com/widget?id=YOUR_SERVER_ID&theme=light',
                "width": "350",
                "height": "500",
                "allowtransparency": "true",
                "frameborder": "0",
                "id": "chatwidget",
                "name": "chatwidget"
                
            })])])]);
        });
    }
});</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 What do user trust levels do?

Display the button only for users who belong to staff (admins + mods) [@Neuferkar]

<script>Discourse._registerPluginCode('0.8.9', function (api) {
   const user = Discourse.User.current(); 
    if (user !== null && user.staff) {  
        api.decorateWidget('header-icons:before', function (helper) {
            return helper.h('li.dropdown#chat', [helper.h('button.dropdown-toggle#chat-button.icon', {
                'attributes': {
                    "type": "button",
                    "data-toggle": "dropdown",
                    "title": "discord chat for staff members"
                } }, helper.h('i.fa.fa-comments-o.chat-button-icon.d-icon')), helper.h('ul.dropdown-menu#chat', [helper.h('li', [helper.h('iframe', {
                "src": 'https://discordapp.com/widget?id=YOUR_SERVER_ID&theme=light',
                "width": "350",
                "height": "500",
                "allowtransparency": "true",
                "frameborder": "0",
                "id": "chatwidget",
                "name": "chatwidget",

            })])])]);
        });
    }
});</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>Discourse._registerPluginCode('0.8.9', function (api) {
   const user = Discourse.User.current(); 
    if (user !== null && user.primary_group_name === "footeam") {  
        api.decorateWidget('header-icons:before', function (helper) {
            return helper.h('li.dropdown#chat', [helper.h('button.dropdown-toggle#chat-button.icon', {
                'attributes': {
                    "type": "button",
                    "data-toggle": "dropdown",
                    "title": "discord chat for footeam members"
                } }, helper.h('i.fa.fa-comments-o.chat-button-icon.d-icon')), helper.h('ul.dropdown-menu#chat', [helper.h('li', [helper.h('iframe', {
                "src": 'https://discordapp.com/widget?id=YOUR_SERVER_ID&theme=light',
                "width": "350",
                "height": "500",
                "allowtransparency": "true",
                "frameborder": "0",
                "id": "chatwidget",
                "name": "chatwidget",

            })])])]);
        });
    }
});</script>

Mobile View

For mobile I use an icon with an invite Url to my server.

Display the button for ALL users (logged in and visitors):

Add this script under /admin/customize/themes inside Mobile/Head tab

<script>Discourse._registerPluginCode('0.8.9', function (api) {
        api.decorateWidget('header-icons:before', function (helper) {
            return helper.h('li', [helper.h('a#chatd-button', {
                "href": 'https://discordapp.com/invite/INVITE_ID',
                "title": 'discord chat',
                "target": '_blank'
                }, helper.h('i.fa.fa-comments-o.chat-button-icon'
            ))]);
        });
    });    
</script>

Make sure to change the Url here:

  • "href": 'https://discordapp.com/invite/INVITE_ID', with an Url generated for the invite to the main channel (generally a Welcome channel).
    Note that Url’s can be both in this format https://discordapp.com/invite/INVITE_ID (old links) or in this format https://discord.gg/INVITE_ID (new links)
  • remember to generate an invitations of infinite duration, so it will always be valid and you will not have to change it again

Under Mobile/CSS

#chatd-button {
    display: block;
    padding: 3px;
    text-decoration: none;
    color: #fff;
    cursor: pointer;
    border-top: 1px solid transparent;
    border-left: 1px solid transparent;
    border-right: 1px solid transparent;
    transition: all linear .15s;
    width: 32px;
    height: 32px;
    font-size: 1.714em;
    line-height: 32px;
}

#chatd-button:hover {
    color: #222;
    background-color: #e9e9e9;
    border-top: 1px solid transparent;
    border-left: 1px solid transparent;
    border-right: 1px solid transparent;
}

You can use all of the variations of this script that are for the Desktop version, e.g.:

Display the button only to logged in users

<script>Discourse._registerPluginCode('0.8.9', function (api) {
        const user = Discourse.User.current();
        if (user !== null) {
            api.decorateWidget('header-icons:before', function (helper) {
                return helper.h('li', [helper.h('a#chatd-button', {
                    "href": 'https://discordapp.com/invite/INVITE_ID',
                    "title": 'chat',
                    "target": '_blank'
                    }, helper.h('i.fa.fa-comments-o.chat-button-icon'
                ))]);
            });
        }    
    });
</script>

Just remember to edit these two lines according to your needs:

    const user = Discourse.User.current();
    if (user !== null) {

Preview of post not loading because force https was not set
#2

omg. this looks great.

i’ve connected patreon to discourse and have offered a discord server as a reward for their subscription. would be neat to show just for certain patreon-level rewards.

will have to review what you’ve done… :thinking:

AMAZE!


(Daniela) #3

Feel free to edit the script if you find a solution, the OP is wiki!


#4

unnamed

mobile has some issues.


(Daniela) #5

As I said:

You need to add some CSS specific for mobile device, it is not so difficult, I will see tomorrow if I can add it.


(Daniela) #6

@Neuferkar solved the problems relative to the script for primary group.
@8BIT try it, it works for me, if it works even for you I will update the OP. We have removed the toLowerCase part, so it’s case insensitive, in case use the name of your group with uppercase.

    <script>Discourse._registerPluginCode('0.8.9', function (api) {
       const user = Discourse.User.current(); 
        if (user !== undefined || user.primary_group_name() === "FooTeam") {  
            api.decorateWidget('header-icons:before', function (helper) {
                return helper.h('li.dropdown#chat', [helper.h('button.dropdown-toggle#chat-button.icon', {
                    'attributes': {
                        "type": "button",
                        "data-toggle": "dropdown",
                        "title": "discord chat"
                    } }, helper.h('i.fa.fa-comments-o.chat-button-icon.d-icon')), helper.h('ul.dropdown-menu#chat', [helper.h('li', [helper.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",

                })])])]);
            });
        }
    });</script> 

(Kane York) #7

Mobile should probably just link to the server - the mobile app supports backgrounded voice chat.


#8

it still shows for folks who aren’t in the primary group. doing some more tests…


created new user:

50 PM

clearly no default group. shows discord:


(Daniela) #9

I’ve just create an account to your forum and I can’t see the chat icon. The stylesheet is still active?

PS. try to clean the browser cache


#10

try the Classic theme.


(Daniela) #11

Can you send me the script that you are using, as it is, via PM?
I can’t repro this issue on my forum


(Sam) #12

Nicely done!

One question - did you have to set something to reload the iframe for the widget? I’ve been running something similar on my site, but I had to add some junk in /body to get the iframe to reload every so often, the widget would never update itself otherwise unless I fully reloaded the page.


(Daniela) #13

No, I’m not using it in production yet, so far I’ve only tested it and I did not notice this problem.

Please, feel free to update the OP with your fix for reload the widget :+1:


(Sam) #14

I’ve just been using this simple kludge in </body>:

<script>
    function reloadIFrame() {
        var disc = document.getElementById("chatwidget");
        if (disc) {
            disc.src="widget source url";
        }
    }
    window.setInterval("reloadIFrame();", 90000);
</script>

Not a bad time to try and figure out what the best practice is though, I just went with reloading the iframe every 1.5 mins but not 100% sure. The discord server only updates the widget page every… 5 minutes, I think it was (made writing this code annoying enough, anyway)? So I thought reloading every time you dropped down the menu might be too much and it’s a bit klunky since you have to watch the iframe reload, but it could work too.

Didn’t really have any other thoughts on how to approach it, so here we are. :slight_smile:

In terms of drawbacks, reloading the iframe will cause your site’s favicon to do the little refresh animation, and I guess it takes up a bit of data each time too, although I would think not much? I’ve been using it without much issue, though.


(Daniela) #15

I don’t understand why if I use:

    const user = Discourse.User.current();
    if (user !== null && user.moderator)

to target only mods (and in the same way staff and admin automatic group) it works, but when I use:

    const user = Discourse.User.current();
    if (user !== null && user.primary_group_name === "FooTeam")

the script won’t work.

Do you have any idea @techAPJ and @cpradio?

Thanks


(cpradio) #16

When you run Discourse.User.current() in the browser’s dev console, what is the exact value you see for primary_group_name?


(Daniela) #17

primary_group_name is all lowercase :no_mouth:

Changing

if (user !== null && user.primary_group_name === "FooTeam")

to

if (user !== null && user.primary_group_name === "footeam")

seems to work now

I can not believe I wasted 1 day on such a stupid mistake…

PS thanks for the help!


(Daniela) #18

Added instructions for Mobile View (button + invite Url).

On smartphone the widget is virtually useless imho.


(Sydney) #19

@Trash If I want to use a custom icon image instead of a FontAwesome Icon, how would I change that? Would it have to be done in the script or in the css? (examples would be most appreciated) :slight_smile:


(Daniela) #20

The easiest way is via CSS:

.fa.fa-comments-o.chat-button-icon.d-icon::before {
    content: url("your-image-url");
}

Upload your image inside the topic for site assets in your staff category, and get the url directly from your site.
It should works even if you use a relative path:

  • absolute path: https://your-site.com/uploads/default/original/2X/6/xxxxxxxxxxxxxxxxxxxxxxxxxxxx.png
  • relative path: /uploads/default/original/2X/6/xxxxxxxxxxxxxxxxxxxxxxxxxxxx.png