SpeedyMessages User Script. TESTERS WANTED


  • FoxDev

    Okay, i'm sick and tired of getting 404 NOT FOUND when trying to visit /my/messages

    so i wrote this.

    it's ugly, but it works as far as i can tell.

    Testers wanted, also if someone wants to make it prettier and maybe autoload pages..... that would be awesome!

    (Paging @Yamikuronue who wanted this, also @abarker and @izzion who will probably find this useful for modding the current mafia game)

    // ==UserScript==
    // @name         SpeedyMessages
    // @namespace    http://what.thedailywtf.com/*
    // @version      0.1
    // @description  SpeedyPM views for people in a hurry!
    // @author       accalia
    // @match        https://what.thedailywtf.com/*
    // @grant        none
    // ==/UserScript==
    /* jshint -W097 */
    'use strict';
    /*global history:true, Discourse: true */
    
    var USER = Discourse.User.current().username;
    var SPEEDY_BUTTON = '<div style="float:right; font-size: 1.6em;margin-top: .4em;"><a class="SpeedyPMTrigger" style="color: #999;"><i class="fa fa-envelope"></i></a></div>';
    var TABLE_START = '<table style="width:100%" class="ember-view topic-list user-main">';
    var HEADER = '<thead><tr>' +
        '<th data-sort-order="default" class="default">Private Message</th>' +
        '<th data-sort-order="posts" class="posts num">Replies</th>' +
        '<th data-sort-order="participants" class="participants">Users</th>' +
        '<th data-sort-order="likes" class="likes num">Likes</th>' +
        '<th data-sort-order="views" class="views num">Views</th>' +
        '<th data-sort-order="activity" class="activity num">Activity</th>' +
        '</tr></thead>';
    var TABLE_END = '</table>';
    var NEXT_BUTTON = '<div class="SpeedyMessages-NextPage">Loading Next Page...</div>';
    
    var ROW_TEMPLATE = '<tr class="topic-list-item" data-topic-id="TOPIC_ID" data-page="TOPIC_PAGE">' +
        '<td class="main-link clearfix" colspan="1"><a href="TOPIC_LINK" class="title visited">TOPIC_TITLE</a> TOPIC_NEW TOPIC_UNREAD</td>' +
        '<td class="num posts-map posts heatmap-" title="This topic has TOPIC_REPLIES replies"><a href="" class="posts-map badge-posts heatmap-">TOPIC_REPLIES</a></td>' +
        '<td class="posters">TOPIC_POSTERS</td>' +
        '<td class="num likes"><a href="/t/message/TOPIC_ID/1?filter=summary"><span class="number" title="TOPIC_LIKES">TOPIC_LIKES</span> <i class="fa fa-heart"></i></a></td>' +
        '<td class="num views "><span class="number" title="this topic has been viewed TOPIC_VIEWS times">TOPIC_VIEWS</span></td>' +
        '<td class="num age activity"><span class="relative-date" data-time="TOPIC_LAST_AGE" data-format="tiny">TOPIC_LAST_AGE_TEXT</span></td>' +
        '</tr>';
    var USER_TEMPLATE = '<a href="/users/NAME" data-user-card="NAME" class="avatar EXTRAS"><img alt="" width="25" height="25" src="AVATAR" class="avatar" title="NAME"></a>';
    var NEW_TEMPLATE = '<a href="TOPIC_LINK" class="badge badge-notification new-posts" title="there are NUMBER new posts in this topic">NUMBER</a>';
    var UNREAD_TEMPLATE = '<a href="TOPIC_LINK" class="badge badge-notification unread" title="you have NUMBER unread posts in this topic">NUMBER</a>';
    
    
    var LOADING = false;
    
    function replaceKeys(input, keys) {
        return input.replace(/[A-Z]+(_[A-Z]+)*/g, function (key) {
            return keys[key] !== undefined ? keys[key] : key;
        });
    }
    
    function loadMessages(page) {
        LOADING = true;
        $.ajax({
            url: 'https://what.thedailywtf.com/topics/private-messages/' + USER + '.json?page=' + page,
            method: 'GET',
            headers: {
                accept: 'application/json, text/javascript, */*; q=0.01'
            },
            success: function (data) {
                if (data.topic_list.topics.length === 0){
                    // All done!
                    $('.SpeedyMessages-NextPage').hide();
                    return;
                }
                var users = {};
                data.users.forEach(function (u) {
                    u.avatar_template = u.avatar_template.replace('{size}', '25');
                    users[u.id] = u;
                });
                $('#SpeedyMessages > table').append(data.topic_list.topics.map(function (t) {
                    var posters = [];
                    t.posters.forEach(function (u) {
                        posters.push(replaceKeys(USER_TEMPLATE, {
                            NAME: users[u.user_id].username,
                            AVATAR: users[u.user_id].avatar_template,
                            EXTRAS: u.extras
                        }));
                    });
                    var fuzzy = (new Date().getTime() - new Date(t.last_posted_at).getTime()) / 60 / 1000;
                    if (fuzzy < 60) {
                        fuzzy = Math.floor(fuzzy) + 'm';
                    } else {
                        fuzzy /= 60;
                        if (fuzzy < 24) {
                            fuzzy = Math.floor(fuzzy) + 'h';
                        } else {
                            fuzzy = Math.floor(fuzzy / 24) + 'd';
                        }
                    }
                    var new_posts = t.unseen ? t.posts_count : t.new_posts;
    
                    return {
                        TOPIC_PAGE: page,
                        TOPIC_TITLE: t.title,
                        TOPIC_ID: t.id,
                        TOPIC_LINK: '/t/' + t.slug + '/' + t.id + '/' + t.last_read_post_number,
                        TOPIC_REPLIES: t.posts_count,
                        TOPIC_VIEWS: t.views,
                        TOPIC_NEW: new_posts === 0 ? '' : NEW_TEMPLATE.replace(/NUMBER/g, new_posts),
                        TOPIC_UNREAD: t.unread === 0 ? '' : UNREAD_TEMPLATE.replace(/NUMBER/g, t.unread),
                        TOPIC_LAST_AGE: new Date(t.last_posted_at).getTime(),
                        TOPIC_LAST_AGE_TEXT: fuzzy,
                        TOPIC_LAST_USER: t.last_poster_username,
                        TOPIC_POSTERS: posters.join(''),
                        TOPIC_LIKES: t.like_count
                    };
                }).map(function (t) {
                    return replaceKeys(replaceKeys(ROW_TEMPLATE, t), t);
                }).join(''));
                LOADING = false;
            }
        });
    }
    
    function init() {
        $('[role=navigation]').closest('.panel').after(SPEEDY_BUTTON);
        $(document).on('click', '.SpeedyPMTrigger', null, showSpeedyMessages);
        $(document).on('click', '#SpeedyMessages button.load', null, function () {
            loadMessages($(this).data('page'), $(this).data('target'));
        });
        return false;
    }
    
    function showSpeedyMessages() {
        if ($('#SpeedyMessages:visible').length > 0) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
            return;
        }
        history.pushState({
            speedy: true
        }, 'Speedy Messages');
        state = history.state;
        if ($('#SpeedyMessages').length === 0) {
            $('#main-outlet').append('<div id="SpeedyMessages"></div>');
    
        }
        $('#main-outlet > *').hide();
        $('#SpeedyMessages').show();
        $('#SpeedyMessages').html(TABLE_START + HEADER + TABLE_END + NEXT_BUTTON);
        loadMessages(0);
    }
    var state = history.state;
    
    function checker() {
        if (history.state != state) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
        }
        if (!LOADING && $('#SpeedyMessages:visible').length > 0 && isVisible('.SpeedyMessages-NextPage')) {
            loadMessages(1 + parseInt($('#SpeedyMessages tr:last').data('page'), 10));
        }
    }
    setInterval(checker, 100);
    
    function isVisible(selector) {
        var vpH = $(window).height(), // Viewport Height
            st = $(window).scrollTop(), // Scroll Top
            y = $(selector).offset().top;
        return ((y < (vpH + st)) && (y > st));
    }
    init();
    

  • FoxDev

    ah. do note that i couldn't figure out how to make it generic so you'll need to swap my name out for yours in that first URL there.

    if somone has a good way to do that automatically i'd love to incorperate that too i figured it out.



  • Are we still doing phrasing?


  • FoxDev

    @swayde said:

    Are we still doing phrasing?

    ara?

    phrasing?



  • @accalia said:

    Speedy pms


  • FoxDev

    i suppose i could be convinced to change the name.....



  • I thought it was intentional clickbait..


  • FoxDev

    it was not.


  • FoxDev

    Rename complete.....


  • FoxDev

    *installs it*
    *clicks new header button*
    *w- oh, it's loaded already 😄*


  • FoxDev

    @RaceProUK said:

    w- oh, it's loaded already 😄

    and now you know where i got the name from. :-D



  • @accalia said:

    if somone has a good way to do that automatically i'd love to incorperate that too

    Other possible option: Discourse.User.current().username



  • Just a few issues I've noticed so far:

    • Navigation from the menus doesn't seem to work while you are in the SpeedyMessages interface.
    • I would expect that opening another page of messages would close the first page, but it doesn't. Not a breaking issue, but it does break expectations.
    • It would be nice to see who else is in the PM (if the JSON provides that) instead of just latest activity. As you are aware, it is not unusual for PMs to have the same title, and sometimes you need to see who is in the PM to identify which is the one you want.

  • Winner of the 2016 Presidential Election

    @swayde said:

    Are we still doing phrasing?

    What about "that's what she said", can we at least do that?


  • FoxDev

    @abarker said:

    Navigation from the menus doesn't seem to work while you are in the SpeedyMessages interface.

    got a test case? i'll see what i can do if i can reproduce

    @abarker said:

    I would expect that opening another page of messages would close the first page, but it doesn't. Not a breaking issue, but it does break expectations.
    hmm.... i'll take that under advice. should be able to work something out anyway.

    @abarker said:

    It would be nice to see who else is in the PM (if the JSON provides that) instead of just latest activity.
    it is available, it was just annoying to access so i shelved it for the time being. it's on my list to add in again, also with proper relative dates too.


  • FoxDev

    @ChaosTheEternal said:

    Other possible option: Discourse.User.current().username

    hmm... that's probably better than finding a user link and parsing the URL.....



  • @accalia said:

    Navigation from the menus doesn't seem to work while you are in the SpeedyMessages interface.

    got a test case? i'll see what i can do if i can reproduce

    1. Go to the speedy menus interface:

    2. Open the user menu:

    3. Click a notification for the thread you were in when you opened SpeedyMessages. Expect to navigate to the notification target, but stay in the SpeedyMessage UI.

    I narrowed it down when I did my repro.


  • FoxDev

    Would History.pushState() be useful for fixing this?



  • *cough* you may want to exercise caution posting screenshots of your PMs when you're modding a Mafia game *cough*

    (as far as I know, nothing terrible has leaked, but still)


  • FoxDev

    @abarker said:

    3. Click a notification for the thread you were in when you opened SpeedyMessages.

    ah. updated version in the OP should fix that

    @RaceProUK said:

    Would History.pushState() be useful for fixing this?
    it was. thanks!


  • FoxDev

    @accalia said:

    hmm.... i'll take that under advice. should be able to work something out anyway.

    Implemented in the current version. not sure i like the new behavior any better...

    Let me know what you think?



  • I made sure nothing pertaining to the game state was included in the screenshot. ;)

    Everything there is either public information or doesn't provide any meta-information about the game. It's part of the reason I named all the Role PMs the same.


  • I survived the hour long Uno hand

    I did that too, but there's always the risk that, say, you'll leak the last poster in the scumthead >.>



  • Last poster in the scum thread is me. And the rules forbid them from posting during the day phase, so ...


  • I survived the hour long Uno hand

    Ah, so you are trying that out :)

    (Should you be telling me this? I guess it can't hurt but...)


  • Java Dev

    It figures - he pretty explicitly mentioned in the opening of the day thread that no town nighttime posting would be allowed, and I believe he's mentioned in the postdiscussion of mafia III as well that he wanted to try enforcing non-posting of town at night/mafia at day, mainly to weaken mafia.



  • @Yamikuronue said:

    Ah, so you are trying that out 😄

    (Should you be telling me this? I guess it can't hurt but...)

    It's listed in the Game OP. ;)


  • FoxDev

    new version in OP. Now it's a toggle!


  • FoxDev

    another new version!

    // ==UserScript==
    // @name         SpeedyMessages
    // @namespace    http://what.thedailywtf.com/*
    // @version      0.1
    // @description  SpeedyPM views for people in a hurry!
    // @author       accalia
    // @match        https://what.thedailywtf.com/*
    // @grant        none
    // ==/UserScript==
    /* jshint -W097 */
    'use strict';
    /*global history:true, Discourse: true */
    
    var ROW_TEMPLATE = '<tr class="topic-list-item" data-topic-id="TOPIC_ID">' +
        '<td class="main-link clearfix" colspan="1"><a href="TOPIC_LINK" class="title visited">TOPIC_TITLE</a> (TOPIC_NEW/TOPIC_UNREAD)</td>' +
        '<td class="num posts-map posts heatmap-" title="This topic has TOPIC_REPLIES replies"><a href="" class="posts-map badge-posts heatmap-">TOPIC_REPLIES</a></td>' +
        '<td class="posters">TOPIC_POSTERS</td>'+
        '<td class="num views "><span class="number" title="this topic has been viewed TOPIC_VIEWS times">TOPIC_VIEWS</span></td>' +
        '<td class="num age activity"><span class="relative-date" data-time="TOPIC_LAST_AGE" data-format="tiny">????</span></td>' +
        '</tr>';
    var HEADER = '<thead><tr>' +
        '<th data-sort-order="default" class="default">Private Message</th>' +
        '<th data-sort-order="posts" class="posts num">Replies</th>' +
        '<th data-sort-order="views" class="views num">Views</th>' +
        '<th data-sort-order="activity" class="activity num">Activity</th>' +
        '</tr></thead>';
    var USER_TEMPLATE = '<a href="/users/NAME" data-user-card="NAME" class="EXTRAS"><img alt="" width="25" height="25" src="AVATAR" class="avatar" title="NAME"></a>';
    var USER = Discourse.User.current().username;
    
    function replaceKeys(input, keys) {
        return input.replace(/[A-Z]+(_[A-Z]+)*/g, function (key) {
            return keys[key];
        });
    }
    
    function loadMessages(page, target) {
        $.ajax({
            url: 'https://what.thedailywtf.com/topics/private-messages/' + USER + '.json?page=' + page,
            method: 'GET',
            headers: {
                accept: 'application/json, text/javascript, */*; q=0.01'
            },
            success: function (data) {
                var users = {};
                data.users.forEach(function (u) {
                    u.avatar_template = u.avatar_template.replace('{size}', '25');
                    users[u.id] = u;
                });
                var t = $(target);
                $('.speedyPage .container').hide();
                $('.speedyPage .button').show();
                t.find('.container').html('<table style="width:100%" class="ember-view topic-list">' + HEADER + data.topic_list.topics.map(function (t) {
                    var posters = [];
                    t.posters.forEach(function (u) {
                        posters.push(replaceKeys(USER_TEMPLATE, {
                            NAME: users[u.user_id].username,
                            AVATAR: users[u.user_id].avatar_template,
                            EXTRAS: u.extras
                        }));
                    });
                    return {
                        TOPIC_TITLE: t.title,
                        TOPIC_ID: t.id,
                        TOPIC_LINK: '/t/' + t.slug + '/' + t.id + '/' + t.last_read_post_number,
                        TOPIC_REPLIES: t.posts_count,
                        TOPIC_VIEWS: t.views,
                        TOPIC_NEW: t.unseen ? t.posts_count : t.new_posts,
                        TOPIC_UNREAD: t.unread,
                        TOPIC_LAST_AGE: new Date(t.last_posted_at).getTime(),
                        TOPIC_LAST_USER: t.last_poster_username,
                        TOPIC_POSTERS: posters.join('')
                    };
                }).map(function (t) {
                    return replaceKeys(ROW_TEMPLATE, t);
                }).join('') + '</table>');
                t.find('.container').show();
                t.find('.button').hide();
            }
        });
    }
    
    function init() {
        $('[role=navigation]').closest('.panel').after('<div style="float:right; font-size: 1.6em;margin-top: .4em;"><a class="SpeedyPMTrigger"><i class="fa fa-envelope"></i></a></div>');
        $(document).on('click', '.SpeedyPMTrigger', null, showSpeedyMessages);
        $(document).on('click', '#SpeedyMessages button.load', null, function () {
            loadMessages($(this).data('page'), $(this).data('target'));
        });
        return false;
    }
    
    function showSpeedyMessages() {
        if ($('#SpeedyMessages:visible').length > 0) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
            return;
        }
        var pages = 10;
        var rows = [];
        for (var i = 0; i < pages; i += 1) {
            rows.push('<tr><td class="speedyPage" id="PMPage' + i + '"><div class="button"><button class=load data-page="' + i + '" data-target="#PMPage' + i + '">Load Page ' + i + '</div><div class="container"></div></td></tr>');
        }
        history.pushState({
            speedy: true
        }, 'Speedy Messages');
        state = history.state;
        if ($('#SpeedyMessages').length === 0) {
            $('#main-outlet').append('<div id="SpeedyMessages"></div>');
    
        }
        $('#main-outlet > *').hide();
        $('#SpeedyMessages').show();
        $('#SpeedyMessages').html('<table style="width:100%">' + rows.join('') + '</table>');
        loadMessages(0, '#PMPage0');
    }
    var state = history.state;
    
    function checker() {
        if (history.state != state) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
        }
    }
    setInterval(checker, 100);
    
    init();
    

    i have no idea what triggers the calculation but eventually discourse will come along and calculate the relative times in place of the ???? marker.

    DONE:

    • The trigger button is now a toggle
    • Add avatars of participants (usercards don't work.... why?)

    TODO:

    • Add the likes column back in
    • make it an infiniscroll list.
    • figure out why the latest poster style is wonky

  • FoxDev

    @accalia said:

    another new version!

    You're going through versions slightly quicker than Chrome! 😛


  • FoxDev

    @accalia said:

    figure out why the latest poster style is wonky

    Change
    class="latest"
    to
    class="avatar latest"

    Edit: Diff time!
    Line 58:

    -                        EXTRAS: u.extras
    +                        EXTRAS: u.extras + ' avatar'
    

    Alternatively, and this is probably better, line 27:

    -var USER_TEMPLATE = '<a href="/users/NAME" data-user-card="NAME" class="EXTRAS"><img alt="" width="25" height="25" src="AVATAR" class="avatar" title="NAME"></a>';
    +var USER_TEMPLATE = '<a href="/users/NAME" data-user-card="NAME" class="avatar EXTRAS"><img alt="" width="25" height="25" src="AVATAR" class="avatar" title="NAME"></a>';
    

  • FoxDev

    ICHIBAN!

    #INFINISCROLL GET!!!!!!!!!!!!!!!!!!

    // ==UserScript==
    // @name         SpeedyMessages
    // @namespace    http://what.thedailywtf.com/*
    // @version      0.1
    // @description  SpeedyPM views for people in a hurry!
    // @author       accalia
    // @match        https://what.thedailywtf.com/*
    // @grant        none
    // ==/UserScript==
    /* jshint -W097 */
    'use strict';
    /*global history:true, Discourse: true */
    
    var USER = Discourse.User.current().username;
    var SPEEDY_BUTTON = '<div style="float:right; font-size: 1.6em;margin-top: .4em;"><a class="SpeedyPMTrigger"><i class="fa fa-envelope"></i></a></div>';
    var TABLE_START = '<table style="width:100%" class="ember-view topic-list user-main">';
    var HEADER = '<thead><tr>' +
        '<th data-sort-order="default" class="default">Private Message</th>' +
        '<th data-sort-order="posts" class="posts num">Replies</th>' +
        '<th data-sort-order="participants" class="participants">Users</th>' +
        '<th data-sort-order="likes" class="likes num">Likes</th>' +
        '<th data-sort-order="views" class="views num">Views</th>' +
        '<th data-sort-order="activity" class="activity num">Activity</th>' +
        '</tr></thead>';
    var TABLE_END = '</table>';
    var NEXT_BUTTON = '<button class="SpeedyMessages-NextPage">Loading Next Page...</button>';
    
    var ROW_TEMPLATE = '<tr class="topic-list-item" data-topic-id="TOPIC_ID" data-page="TOPIC_PAGE">' +
        '<td class="main-link clearfix" colspan="1"><a href="TOPIC_LINK" class="title visited">TOPIC_TITLE</a> (TOPIC_NEW/TOPIC_UNREAD)</td>' +
        '<td class="num posts-map posts heatmap-" title="This topic has TOPIC_REPLIES replies"><a href="" class="posts-map badge-posts heatmap-">TOPIC_REPLIES</a></td>' +
        '<td class="posters">TOPIC_POSTERS</td>' +
        '<td class="num likes"><a href="/t/message/TOPIC_ID/1?filter=summary"><span class="number" title="TOPIC_LIKES">TOPIC_LIKES</span> <i class="fa fa-heart"></i></a></td>' +
        '<td class="num views "><span class="number" title="this topic has been viewed TOPIC_VIEWS times">TOPIC_VIEWS</span></td>' +
        '<td class="num age activity"><span class="relative-date" data-time="TOPIC_LAST_AGE" data-format="tiny">TOPIC_LAST_AGE_TEXT</span></td>' +
        '</tr>';
    var USER_TEMPLATE = '<a href="/users/NAME" data-user-card="NAME" class="avatar EXTRAS"><img alt="" width="25" height="25" src="AVATAR" class="avatar" title="NAME"></a>';
    
    var LOADING = false;
    
    function replaceKeys(input, keys) {
        return input.replace(/[A-Z]+(_[A-Z]+)*/g, function (key) {
            return keys[key];
        });
    }
    
    function loadMessages(page) {
        LOADING = true;
        $.ajax({
            url: 'https://what.thedailywtf.com/topics/private-messages/' + USER + '.json?page=' + page,
            method: 'GET',
            headers: {
                accept: 'application/json, text/javascript, */*; q=0.01'
            },
            success: function (data) {
                var users = {};
                data.users.forEach(function (u) {
                    u.avatar_template = u.avatar_template.replace('{size}', '25');
                    users[u.id] = u;
                });
                $('#SpeedyMessages > table').append(data.topic_list.topics.map(function (t) {
                    var posters = [];
                    t.posters.forEach(function (u) {
                        posters.push(replaceKeys(USER_TEMPLATE, {
                            NAME: users[u.user_id].username,
                            AVATAR: users[u.user_id].avatar_template,
                            EXTRAS: u.extras
                        }));
                    });
                    var fuzzy = (new Date().getTime() - new Date(t.last_posted_at).getTime()) / 60 / 1000;
                    if (fuzzy < 60) {
                        fuzzy = Math.floor(fuzzy) + 'm';
                    } else {
                        fuzzy /= 60;
                        if (fuzzy < 24) {
                            fuzzy = Math.floor(fuzzy) + 'h';
                        } else {
                            fuzzy = Math.floor(fuzzy / 24) + 'd';
                        }
                    }
                    return {
                        TOPIC_PAGE: page,
                        TOPIC_TITLE: t.title,
                        TOPIC_ID: t.id,
                        TOPIC_LINK: '/t/' + t.slug + '/' + t.id + '/' + t.last_read_post_number,
                        TOPIC_REPLIES: t.posts_count,
                        TOPIC_VIEWS: t.views,
                        TOPIC_NEW: t.unseen ? t.posts_count : t.new_posts,
                        TOPIC_UNREAD: t.unread,
                        TOPIC_LAST_AGE: new Date(t.last_posted_at).getTime(),
                        TOPIC_LAST_AGE_TEXT: fuzzy,
                        TOPIC_LAST_USER: t.last_poster_username,
                        TOPIC_POSTERS: posters.join(''),
                        TOPIC_LIKES: t.like_count
                    };
                }).map(function (t) {
                    return replaceKeys(ROW_TEMPLATE, t);
                }).join(''));
                LOADING = false;
            }
        });
    }
    
    function init() {
        $('[role=navigation]').closest('.panel').after(SPEEDY_BUTTON);
        $(document).on('click', '.SpeedyPMTrigger', null, showSpeedyMessages);
        $(document).on('click', '#SpeedyMessages button.load', null, function () {
            loadMessages($(this).data('page'), $(this).data('target'));
        });
        return false;
    }
    
    function showSpeedyMessages() {
        if ($('#SpeedyMessages:visible').length > 0) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
            return;
        }
        history.pushState({
            speedy: true
        }, 'Speedy Messages');
        state = history.state;
        if ($('#SpeedyMessages').length === 0) {
            $('#main-outlet').append('<div id="SpeedyMessages"></div>');
    
        }
        $('#main-outlet > *').hide();
        $('#SpeedyMessages').show();
        $('#SpeedyMessages').html(TABLE_START + HEADER + TABLE_END + NEXT_BUTTON);
        loadMessages(0);
    }
    var state = history.state;
    
    function checker() {
        if (history.state != state) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
        }
        if (!LOADING && $('#SpeedyMessages:visible').length > 0 && isVisible('.SpeedyMessages-NextPage')) {
            loadMessages(1 + parseInt($('#SpeedyMessages tr:last').data('page'), 10));
        }
    }
    setInterval(checker, 100);
    
    function isVisible(selector) {
        var vpH = $(window).height(), // Viewport Height
            st = $(window).scrollTop(), // Scroll Top
            y = $(selector).offset().top;
        return ((y < (vpH + st)) && (y > st));
    }
    init();
    

  • FoxDev

    Now we're at the bikeshedding stage, here's a fix for the button colour.

    Line 15:

    -var SPEEDY_BUTTON = '<div style="float:right; font-size: 1.6em;margin-top: .4em;"><a class="SpeedyPMTrigger"><i class="fa fa-envelope"></i></a></div>';
    +var SPEEDY_BUTTON = '<div style="float:right; font-size: 1.6em;margin-top: .4em;"><a class="SpeedyPMTrigger" style="color: #999;"><i class="fa fa-envelope"></i></a></div>';
    

  • FoxDev

    @RaceProUK said:

    style="color: #999;

    added.

    also now the loading message stops when you reach the end of your PMs

    // ==UserScript==
    // @name         SpeedyMessages
    // @namespace    http://what.thedailywtf.com/*
    // @version      0.1
    // @description  SpeedyPM views for people in a hurry!
    // @author       accalia
    // @match        https://what.thedailywtf.com/*
    // @grant        none
    // ==/UserScript==
    /* jshint -W097 */
    'use strict';
    /*global history:true, Discourse: true */
    
    var USER = Discourse.User.current().username;
    var SPEEDY_BUTTON = '<div style="float:right; font-size: 1.6em;margin-top: .4em;"><a class="SpeedyPMTrigger" style="color: #999;"><i class="fa fa-envelope"></i></a></div>';
    var TABLE_START = '<table style="width:100%" class="ember-view topic-list user-main">';
    var HEADER = '<thead><tr>' +
        '<th data-sort-order="default" class="default">Private Message</th>' +
        '<th data-sort-order="posts" class="posts num">Replies</th>' +
        '<th data-sort-order="participants" class="participants">Users</th>' +
        '<th data-sort-order="likes" class="likes num">Likes</th>' +
        '<th data-sort-order="views" class="views num">Views</th>' +
        '<th data-sort-order="activity" class="activity num">Activity</th>' +
        '</tr></thead>';
    var TABLE_END = '</table>';
    var NEXT_BUTTON = '<div class="SpeedyMessages-NextPage">Loading Next Page...</div>';
    
    var ROW_TEMPLATE = '<tr class="topic-list-item" data-topic-id="TOPIC_ID" data-page="TOPIC_PAGE">' +
        '<td class="main-link clearfix" colspan="1"><a href="TOPIC_LINK" class="title visited">TOPIC_TITLE</a> TOPIC_NEW TOPIC_UNREAD</td>' +
        '<td class="num posts-map posts heatmap-" title="This topic has TOPIC_REPLIES replies"><a href="" class="posts-map badge-posts heatmap-">TOPIC_REPLIES</a></td>' +
        '<td class="posters">TOPIC_POSTERS</td>' +
        '<td class="num likes"><a href="/t/message/TOPIC_ID/1?filter=summary"><span class="number" title="TOPIC_LIKES">TOPIC_LIKES</span> <i class="fa fa-heart"></i></a></td>' +
        '<td class="num views "><span class="number" title="this topic has been viewed TOPIC_VIEWS times">TOPIC_VIEWS</span></td>' +
        '<td class="num age activity"><span class="relative-date" data-time="TOPIC_LAST_AGE" data-format="tiny">TOPIC_LAST_AGE_TEXT</span></td>' +
        '</tr>';
    var USER_TEMPLATE = '<a href="/users/NAME" data-user-card="NAME" class="avatar EXTRAS"><img alt="" width="25" height="25" src="AVATAR" class="avatar" title="NAME"></a>';
    var NEW_TEMPLATE = '<a href="TOPIC_LINK" class="badge badge-notification new-posts" title="there are NUMBER new posts in this topic">NUMBER</a>';
    var UNREAD_TEMPLATE = '<a href="TOPIC_LINK" class="badge badge-notification unread" title="you have NUMBER unread posts in this topic">NUMBER</a>';
    
    
    var LOADING = false;
    
    function replaceKeys(input, keys) {
        return input.replace(/[A-Z]+(_[A-Z]+)*/g, function (key) {
            return keys[key] !== undefined ? keys[key] : key;
        });
    }
    
    function loadMessages(page) {
        LOADING = true;
        $.ajax({
            url: 'https://what.thedailywtf.com/topics/private-messages/' + USER + '.json?page=' + page,
            method: 'GET',
            headers: {
                accept: 'application/json, text/javascript, */*; q=0.01'
            },
            success: function (data) {
                if (data.topic_list.topics.length === 0){
                    // All done!
                    $('.SpeedyMessages-NextPage').hide();
                    return;
                }
                var users = {};
                data.users.forEach(function (u) {
                    u.avatar_template = u.avatar_template.replace('{size}', '25');
                    users[u.id] = u;
                });
                $('#SpeedyMessages > table').append(data.topic_list.topics.map(function (t) {
                    var posters = [];
                    t.posters.forEach(function (u) {
                        posters.push(replaceKeys(USER_TEMPLATE, {
                            NAME: users[u.user_id].username,
                            AVATAR: users[u.user_id].avatar_template,
                            EXTRAS: u.extras
                        }));
                    });
                    var fuzzy = (new Date().getTime() - new Date(t.last_posted_at).getTime()) / 60 / 1000;
                    if (fuzzy < 60) {
                        fuzzy = Math.floor(fuzzy) + 'm';
                    } else {
                        fuzzy /= 60;
                        if (fuzzy < 24) {
                            fuzzy = Math.floor(fuzzy) + 'h';
                        } else {
                            fuzzy = Math.floor(fuzzy / 24) + 'd';
                        }
                    }
                    var new_posts = t.unseen ? t.posts_count : t.new_posts;
    
                    return {
                        TOPIC_PAGE: page,
                        TOPIC_TITLE: t.title,
                        TOPIC_ID: t.id,
                        TOPIC_LINK: '/t/' + t.slug + '/' + t.id + '/' + t.last_read_post_number,
                        TOPIC_REPLIES: t.posts_count,
                        TOPIC_VIEWS: t.views,
                        TOPIC_NEW: new_posts === 0 ? '' : NEW_TEMPLATE.replace(/NUMBER/g, new_posts),
                        TOPIC_UNREAD: t.unread === 0 ? '' : UNREAD_TEMPLATE.replace(/NUMBER/g, t.unread),
                        TOPIC_LAST_AGE: new Date(t.last_posted_at).getTime(),
                        TOPIC_LAST_AGE_TEXT: fuzzy,
                        TOPIC_LAST_USER: t.last_poster_username,
                        TOPIC_POSTERS: posters.join(''),
                        TOPIC_LIKES: t.like_count
                    };
                }).map(function (t) {
                    return replaceKeys(replaceKeys(ROW_TEMPLATE, t), t);
                }).join(''));
                LOADING = false;
            }
        });
    }
    
    function init() {
        $('[role=navigation]').closest('.panel').after(SPEEDY_BUTTON);
        $(document).on('click', '.SpeedyPMTrigger', null, showSpeedyMessages);
        $(document).on('click', '#SpeedyMessages button.load', null, function () {
            loadMessages($(this).data('page'), $(this).data('target'));
        });
        return false;
    }
    
    function showSpeedyMessages() {
        if ($('#SpeedyMessages:visible').length > 0) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
            return;
        }
        history.pushState({
            speedy: true
        }, 'Speedy Messages');
        state = history.state;
        if ($('#SpeedyMessages').length === 0) {
            $('#main-outlet').append('<div id="SpeedyMessages"></div>');
    
        }
        $('#main-outlet > *').hide();
        $('#SpeedyMessages').show();
        $('#SpeedyMessages').html(TABLE_START + HEADER + TABLE_END + NEXT_BUTTON);
        loadMessages(0);
    }
    var state = history.state;
    
    function checker() {
        if (history.state != state) {
            $('#main-outlet > *').show();
            $('#SpeedyMessages').hide();
        }
        if (!LOADING && $('#SpeedyMessages:visible').length > 0 && isVisible('.SpeedyMessages-NextPage')) {
            loadMessages(1 + parseInt($('#SpeedyMessages tr:last').data('page'), 10));
        }
    }
    setInterval(checker, 100);
    
    function isVisible(selector) {
        var vpH = $(window).height(), // Viewport Height
            st = $(window).scrollTop(), // Scroll Top
            y = $(selector).offset().top;
        return ((y < (vpH + st)) && (y > st));
    }
    init();
    

  • FoxDev

    @accalia said:

    style="color: #999;

    added.


    Cool 😄

    One more thing: when you-
    @accalia said:

    also now the loading message stops when you reach the end of your PMs

    Oh, you already thought of it 😊


  • FoxDev

    yeah.... now to solve the jelly potato issue when dismissing the speedy messages window

    although that will probably be fixed by making it a modal instead of "replace the screen"

    though sorting out scrolling on the modal will be interesting....


  • FoxDev

    I see Bootstrap classes on some of the elements; maybe inject a Bootstrap modal? It should handle the scrolling issue for you.


  • FoxDev

    huh.... looks like bootstrap is installed.... this just got a lot easier.


  • FoxDev

    yeah.... modal isn't happening. not with discourse randomly dismissing it when anything causes the underlying page to scroll.


  • FoxDev

    @accalia said:

    discourse randomly dismissing it when anything causes the underlying page to scroll

    Wow, that's just… sucky


  • FoxDev

    yep. not going to be bother with the modals....

    someone else can add that if they want.



  • As someone who might know a few things about writing replacements for private message UIs, I would appreciate it if you pulled a WorseThanFailure and went back to calling this SpeedyPMS.


  • FoxDev

    @ben_lubar said:

    I would appreciate it if you pulled a WorseThanFailure and went back to calling this SpeedyPMS.

    Pull Request Rejected as Invalid.


  • Notification Spam Recipient

    @accalia said:

    .

    Installed this version. Seems really nice!


  • FoxDev

    @Tsaukpaetra said:

    Installed this version. Seems really nice!

    asside from the jellypotatoing that happens if you dismiss the PM list without opening a PM i think i'm onto a winner here.



  • Pros: It has all the bells and whistles of the "real" Messages page, but it is actually accessible!
    Cons: That jellypotato thing.

    Overall, I'd give the current version 9.5/10. 👍


  • Notification Spam Recipient

    @accalia said:

    asside from the jellypotatoing that happens if you dismiss the PM list

    You know, I didn't know it was supposed to be dismissed by clicking the icon again 😊
    Now I know what you were talking about with the Bootstrap dialog thing. ;)

    I don't think you can get rid of the jellypotato without emulating a navigation out of the thread. Really, I think this is an issue with the "scroll reached an end of the page" being delayed or something. If we convinced the page that we actually switched topics that might produce more natural behavior.



  • In my own user script, I used code like this to get a link to the current page:

    var h = document.querySelector("header").offsetHeight;
    var post = Array.apply(0, document.querySelectorAll(".post-cloak"));
    post = post.filter(e => e.getBoundingClientRect().top > h)[0] || post.pop();
    var url = post && post.querySelector(".post-date") || location;
    url = url.href.replace(/^[^:]*:\/+[^\/]+|\?.*/g, "") || "/";
    

    url will contain something like (just a handful of examples):

    /
    /categories
    /users/anotherusername/notifications
    /t/speedymessages-user-script-testers-wanted/54501/47
    

    What I was doing with that was just adding it to the history:

    window.history.replaceState({path: url}, "", url);
    

    But since you need to return to that page, when you need to close your interface, location = url should work. Or, you could possibly push a different history state value, then get it to ajax-load the correct location, but I'll leave that part to you.


  • FoxDev

    @anotherusername said:

    But since you need to return to that page, when you need to close your interface, location = url should work.

    that would cause a full page load, something i'm trying to avoid....

    @anotherusername said:

    Or, you could possibly push a different history state value, then get it to ajax-load the correct location, but I'll leave that part to you.
    already doing that to dismiss the popup on navigation. ;-)

    no i think the only solution is to make my own "modal"

    or call it an edge case as the jelly potato only happens when you open the messages and then dismiss without navigation.....

    oh and i see the composer does indeed show over the PM list. good.


  • I survived the hour long Uno hand

    @accalia said:

    or call it an edge case

    Another edge case:

    1. Be reading a thread, somewhere below the fold
    2. Open the PM list using the plugin
    3. Click on the site logo to return to the home page

    Expected results: The home page loads, or navigation is prevented
    Actual: Apparently nothing, until you close the PM plugin.


Log in to reply