RIP TweetDeck! Long Live TweetDeck!

There’s been a lot going on in the world of TweetDeck over the past few days and a lot of it has been mis-represented in the public press. Hopefully this should clear up the confusion.

TweetDeck is shutting down some old applications and concentrating instead on the latest web & desktop offerings.

Also, the old company that was TweetDeck has been struck from the registry. However, TweetDeck is now owned and operated by Twitter, so that has no effect on TweetDeck’s future.

What’s staying

The following versions of TweetDeck will continue to be available and actively developed for the foreseeable future:

It looks like this:
Screen Shot 2013-05-13 at 10.32.15 AM

What’s going

  • TweetDeck AIR (v0.38)
  • Android
  • iOS
  • Facebook integration

I hope that’s cleared things up.

TweetDeck: Link shortening

TweetDeck automatically shortens any urls you Tweet using the t.co service. Shortened urls are 22 characters long.

When it displays your Tweet, TweetDeck looks up the original url and displays that instead of the t.co url so it is clearer where a link you are thinking of clicking on is going to take you.

You can test that link shortening is working by entering a very long link. E.g.

http://this.link.is.very.very.very.long.and.can.not.possibly.fit.into.a.single.tweet.but.thatis.okay.because.TweetDeck.will.shortenit.automatically.com

If you paste that link in to the compose box, it’ll tell you you’ve got 118 characters left.

TweetDeck Filters

TweetDeck offers powerful filters to allow you to find the proverbial needle in a haystack.

Presented below are a few solutions to common problems.

Finding a specific tweet by a specific user

Problem:
Someone Tweeted a Tweet you’d like to find, but it was a few weeks ago so it doesn’t turn up in a search and you don’t want to have to read through weeks of tweets to find it.
Solution:
  1. Create a column for tweets by that user
  2. scroll down until you think the tweet should be in the column
  3. filter the column by a keyword you think was in the tweet.

Finding relevant breaking news

Problem:
Something is happening in the world but, when you add a search column, it’s moving far too fast or is full of too much spam and retweets.
Solution:
  1. Open User filters and select “By: verified users”
  2. Open content filters and select “Retweets: Excluded”
  3. Your timeline should now only contain the latest news from trusted sources.

TweetDeck: Autocorrect

TweetDeck doesn’t support autocorrect itself. However, some browsers and operating systems provide native autocorrect and spelling. Unfortunately, in some cases Mountain Lion takes this a step too far and starts autocorrecting things that don’t need correcting.

You can turn off autocorrect in Mountain Lion either specifically for TweetDeck or across the whole OS.

Changing settings for TweetDeck only

With compose open, select Edit > Spelling and Grammar from the menu and turn off autocorrect.

Changing global settings

Turn off autocorrect
Open System Preferences > Language & Text > Text and uncheck ‘Correct spelling automatically’
Choose correct language
Open System Preferences > Language & Text > Text and set the ‘Spelling’ dropdown to the desired language rather than ‘Automatic by language’.

TweetDeck basics: Column navigation

When you’ve got a lot of columns, using the scroll bars can become rather tedious and confusing. Thankfully, TweetDeck has a few ways to help you find your way.

Column Navigator
Press the ‘Columns’ button at the top of the screen to see a list of your columns. Click the name of the column you want to jump to it.
Page left/right
The arrow buttons to the left & right of the Columns button will scroll a whole page at a time.
Arrow keys
Press left/right to move one column left/right. Your current tweet location is highlighted.
Number keys
If you know the number of the column you want to look at (numbers are in the column header and in the column navigator) you can press 1 – 9 to jump to the first nine columns, or 0 to jump all the way to the last column.

TweetDeck basics: Compose

New tweets

The compose panel is where new Tweets are written. You can access it by pressing ‘N’ or the blue compose icon in the top right corner of the app.

Screen Shot 2013-02-13 at 9.09.35 AM

Along the top of the panel you can select which accounts you want to post from. Your default account should already be selected.

You can select as many accounts as you like to post from.

As you start typing, the character counter shows the number of characters remaining.

Links

When you add a link like “tweetdeck.com” or “www.twitter.com” or “http://www.twitter.com”, TweetDeck recognises the link. When you send your tweet, the link will be automatically shortened using Twitter’s t.co link shortener.

t.co links are 21 characters long. This length is indicated in the character counter.

There is no option to turn off link shortening, but you can switch to bit.ly if you prefer in TweetDeck settings.

Images

You can attach an image to your Tweet by clicking the camera icon. You can only attach one image to a single Tweet.

Scheduling

Click the calendar icon to schedule a Tweet. Select a date and time for the Tweet to go out and click the ‘Tweet’ button. When that time rolls around, TweetDeck will send the post on your behalf.

You don’t need to have TweetDeck open, or even have your computer turned on, to send scheduled Tweets. The Tweets are stored on our servers and sent from there.

NB: You can not currently schedule Tweets with images. The Tweet will be sent as normal but the image will not be attached.

Messages

If you press the Messsage button, Compose switches to Message mode. Enter the username of the intended recipient in the first box, then your message in the second.

You can also enter Message mode by typing M or D or DM as the first word in your tweet.

Flight at TweetDeck

Flight is a new JavaScript framework conjured up by the folks at Twitter (big shout out to @danwrong and the rest of the @flight team). At TweetDeck we have had the opportunity to work with a pre-release version of Flight over the past few months and have now shifted all new development to Flight.

When I first heard about Flight I thought it was a brilliant idea. A simple framework done right. The agnostic nature of modules really appealed, as did the event-driven architecture. Having now used it on a large application, I can say with certainty that it did not disappoint and I’d thoroughly recommend taking a good look.

What I want to talk about here is our experience with Flight at TweetDeck, mainly around our approach in converting TweetDeck to Flight and also how we’ve organised our data and UI components.

TweetDeck: before Flight

TweetDeck’s been around for nearly two years as a JavaScript application (the largest in the Chrome App Store, I believe) and has already undergone two architectural changes. At first, modules were built as straightforward namespaced javascript objects. Understanding that this was perhaps not the best long-term approach the team switched to a klass-based approach and ran the two side by side.

For small applications either one of these techniques can be quite effective in the short term but even at that scale, over time they become hard to maintain and harder to refactor. For large applications it requires serious rigour to stop things getting rapidly out-of-control.

Deep-linking between modules is a big factor in this. TD.vo.Column.get() might make sense in the context of an instance of TD.vo.Column but you just try finding every single reference to a method called ‘get’ in your codebase and you’ll quickly understand why you don’t want to mess around with it, especially when the instances start getting passed around as parameters between modules, creating references like column.get, col.get, etc.

TweetDeck’s modules had become interdependent to the point that we recently found it impossible to create a dependency tree. The spaghetti monster was rearing its ugly head.

TweetDeck: after Flight

To be honest, the codebase is much the same as it was. There certainly isn’t any sense trying to rebuild tens of thousands of lines of code for the sake of it.

We’ve created a new directory in our script folder in which all flight components, tests and libraries sit. The hope is that at some point our flight/app directory will drive the entire app, but that’s a long way off.

Luckily, Flight makes it very easy to bolt new components in on top of an existing structure.

Whenever a method would have referenced another namespaced method, it now fires an event, instantly allowing us to stop worrying about refactoring deeply-linked methods. It’s a lovely feeling, removing all those old namespaced references like

var metadata = TD.controller.columnManager.getColumnByKey(columnKey).getMetadata();
this.doSomething(metadata);

where we make so many assumptions about which objects have been instantiated and which methods they expose. We then replace them with event triggers and listeners that assume nothing about the rest of the application:

this.on('dataColumnMetadata', this.handleColumnMetadata);
this.trigger('uiNeedsColumnMetadata', columnKey);

In fact, our main worry when designing Flight components is sensibly named events.

Event names

I’m not sure we’ve got our event-naming nailed as yet. In fact, our naming conventions seem to be widely disagreed upon within our small team. Despite that, we seem to be managing pretty well.

We followed the basic naming conventions used on Twitter.com and added a few of our own. We have four core types of event:

ui data request
A request from the UI for data. E.g.: uiNeedsTwitterProfile, uiNeedsRelationship
ui user action
An action performed by the user, probably listened to by a data component. E.g.: uiFollowAction, uiBlockAction
ui request
A request for the ui to do something. E.g.: uiShowColumnOptions, uiRemoveColumn, uiCloseModal
ui moment
An interesting moment which is the result of a ui request. Some requests have multiple interesting moments: the start and end of an animation, or even the steps within it. “ui” is followed by the name of the component, then the action E.g.: uiShowingColumnOptions, uiColumnOptionsShown, uiColumnOptionsHidden
data
An event containing data. “data” is usually followed by the name of the component triggering the event. Generally data components only trigger a single data event. E.g.: dataTwitterProfile, dataRelationship

You can see this in action by adding a listener for these events to the document in Chrome’s JavaScript console. E.g.:

$(document).on('dataTwitterUser', function(e, data) {console.log(e, data);});

Then go and look at someone’s profile, or try this:

$(document).trigger('uiShowProfile', {id: 'tbrd'});

Our conventions are still in flux and as a result we have a number of events which don’t fit this model. Luckily, renaming them is an easy process.

Mentioning that, I’m reminded again of one of the great things about Flight: ease of refactoring. One piece of advice I can offer is that not to worry too much about getting everything right first time around.

We spent quite some time at the outset considering how data components would behave, how ui components would talk to them, how big components should be (or how small) and where we should use mixins instead of components.

When it came down to it, we realised we needn’t have bothered. It’s desperately easy to alter a component to be a mixin, or the other way around. It’s simple to change the way a component works internally because nothing else cares. It’s dead simple to break up a data component in to lots of little components because nothing is talking to it directly.

Another reason not to worry about it is that Flight actually seems to promote good code. It seems to be quite hard to write a huge, meandering component – it’s much easier (and much more pleasing) to create a host of tiny little components, each of them performing their own job perfectly.

Event ownership

In the absence of a call stack, we need to make sure that a particular event is one we actually want to listen to. There could be lots of data events being fired, all with the same name but with data we’re not interested in.

We’ve tried to manage this by attaching identifying data with each request which is then attached to the data response.

For example, we currently have two components, search and compose, which use Twiter’s typeahead search. Our typeahead data module sends out events like this:


this.trigger('dataTypeaheadSuggestions', {
query: queryString,
suggestions: suggestions,
datasources: datasources,
dropdownId: dropdownId
});

The dropdown that send the request for suggestions will need to check that the query, datasources and dropdownId all match its request before doing anything with the suggestions provided.

Testing

We’ve built a test framework for Flight with Jasmine. You can see some example Jasmine tests in the library itself, but that’s not the whole story. There are a few things you’ll definitely need to implement if you want to make your life easier.

First, we (Twitter) extended Jasmine to provide two additional define methods, defineComponent and defineMixin, which set-up the component/mixin for you and provide access to the component and its prototype within tests, allowing you to interrogate attributes and invoke methods directly.

Second, we utilised Jasmine-jQuery, another extension for Jasmine which provides the ability to test jQuery objects and, more importantly DOM events.

We then patched the Jasmine-jQuery extension to provide us with better events features.

Whatever test framework you use, it’s essential it is able to test which events were fired, what data was passed with them and which DOM element they were fired upon. It’s also useful to check how many times the event was fired – we’ve seen some annoying bugs as a result of events being fired twice, or in a circular event chain.

Without this, testing Flight components is largely pointless as the events are the interface between components, ie: the thing that needs testing most.

I’m working on a fork of Flight including our Jasmine wrappers and extensions – no idea when it might be ready to open up on github. I’ll update this as and when.

The future of Flight in TweetDeck

We have got no doubts that we’ve picked the right framework for the job. However, we still have a lot of big challenges to overcome. Our core data layer is still firmly entrenched in old-style code and is going to continue to be for some time to come. Producing a plan that allows us to keep that going side-by-side with Flight without duplicating code or confusing responsibilities is not a simple task.

In the meantime, every new feature we build is built with Flight. We refactor old modules only when required, instead creating Flight wrappers around old data components to provide additional functionality.

Once we a significant proportion of an old module’s functionality exists in Flight wrapper components, that would be an appropriate time to move the entire functionality of the component in to Flight.

We’re trying to ensure that any deep-linked references to old components exist solely in our data modules. The UI should be blissfully unaware that the TD namespace ever existed.

The future of Flight

I see Flight components as providing great plug-and-play modules. jQuery’s plugin library was one of the big reasons for its success and it’d be awesome to see something similar happening with Flight. Although most data components will be very unique to your application, ui components could easily be shared between apps. I’m hoping to make a few (including a keyboard shortcut manager and focus manager) available to the community soon.

Flight is massively extensible – ideas such as data-binding would seem to fit it well, suggesting the possibility of a growing library of extensions. I’d love to see a big, community-led extensions & plugins library.

Why not go and get involved.

TweetDeck basics: General Settings

TweetDeck allows you to change a few basic settings such as colour scheme, font and column width in the General Settings panel.

TweetDeck options menu

Opening TweetDeck’s options menu

Click the cog in the top right to show the options menu, then select Settings. See below for descriptions of the settings.

TweetDeck's General Settings panel

TweetDeck’s General Settings panel

Twitter streaming

If you really want to, you can turn off streaming. This will force TweetDeck to update your columns at regular intervals, rather than providing a constant stream of tweets.

Media preview

Where it is able, TweetDeck will show you thumbnails of any media attached to a tweet. If you want, you can turn these off. You’ll still see Twitter Cards when you click through to the tweet detail.

Show notifications at startup

When you startup TweetDeck it will show you notifications for the most recent tweets in columns where you have notifications turned on. You can turn this off here.

Column size

You can choose to use narrow or regular-sized columns. Narrow’s useful if you’ve got a small screen.

Font size

You’ve currently got a choice between small, medium and large font sizes. Increasing the font size only affects text size in tweets; it does not affect menus, buttons or panels such as compose or settings.

Theme

You can switch between light and dark themes here. The light theme can seem very bright on a large screen.

TweetDeck: Known Issues

Following these steps can resolve most issues not in the list below:

  • Upgrade to the latest version (2.7.7 on Web/Chrome, 2.7.1 on Windows/Mac desktop).
  • Clear your browser cache.
  • Disable your plugins. Some plugins interfere with TweetDeck in interesting ways. Add them back one at a time to discover which one causes the issue. Let the developer of that plugin know about the problem.

Known issues

Can’t retweet

You’re probably using v0.38. If this is the case, upgrade to the latest version of TweetDeck.

Windows v2.7.1 freezes

Interesting one, this. For some users, the app occasionally freezes, or bits of it stop working. There’s no fix, nor any known cause. At the moment, the best advice TweetDeck can offer is to use the web or Chrome apps instead.

Lists aren’t showing for you or another user

The Twitter API currently only returns 100 subscriptions (your own lists are included in your subscriptions). If a user has more than 100, some of these will not be displayed in TweetDeck. Unfortunately the user’s own lists are always at the bottom, so they’re the most likely not to be displayed.

You can check your subscriptions on your lists page at http://twitter.com/username/lists

At the moment the best recommended course of action is to cancel a few subscriptions, but this obviously isn’t an option when looking at other users.

Scheduling photos

Currently TweetDeck doesn’t allow you to schedule photos, but it doesn’t tell you this when you try to do so – it merely sends the tweet without the image attached.

Avatars aren’t shown

This generally occurs when you use Chrome or Firefox as your main browser on Windows. If you’ve never visited twitter.com in IE, Windows will block attempts to access some urls from within the TweetDeck desktop app.

There’s a simple fix: open IE and visit twitter.com.

“Unable to initialise login UI. Please contact your friendly dev team for assistance.”

This bug was fixed in version 2.7.0
This error occurs when your local storage is either full or disabled. Solutions:

Windows
Delete the file at “C:\Users\\AppData\Local\twitter\TweetDeck\localStorage”. If you can’t see the AppData folder, follow these instructions to show hidden folders in Vista & Windows 7.
Chrome
On Mac, press CMD/CTRL+SHIFT+J. You should see a panel pop up at the bottom of the screen. At the prompt, type “localStorage.clear()” and press return. Refresh the app to see if it’s worked.

“Result of expression ‘TD.ui.columns’ [undefined] is not an object.”

This bug was fixed in version 2.5.3
You’re running web.tweetdeck.com in Safari, or the Mac desktop application. Either way, upgrade Safari to 5.1.7 or above. The Mac desktop app uses Safari behind the scenes.

Interactions column tweet order is wrong

The interactions list is provided by Twitter in non-chronological order. It’s in the same order as your @Connect page on Twitter.com. When you have TweetDeck open, new interactions are added to the top of the column as they are received but when you refresh, they’ll be grouped by type rather than by time.

Tweet Action buttons don’t show for some tweets

This bug appears to have be resolved in Chrome 25.0.1364.160

When hovering over a tweet, you should see buttons for reply, favorite, retweet and the more actions menu. Currently if you’re running Chrome Beta or Canary these buttons won’t appear for some tweets. It looks like this is a bug in Chrome and is likely to be fixed soon. In the meantime, I’d advise using the latests stable release instead.

Deleted column don’t stay deleted

This bug was fixed in TweetDeck version 2.7.0

When you delete a column and then reload the app, you may find that the column is back. This tends to happen more if you are deleting a lot of columns. There’s no real fix for this at the moment, but a bit of patience can help a lot. Try the following:

  1. Open TweetDeck
  2. Wait for 20 – 30 seconds
  3. Delete a column (or a few)
  4. Wait 20-30 seconds
  5. Restart TweetDeck

It seems that if you try to start deleting columns too soon after the app is opened, it fails and will continue to fail until restarted. Also, it can sometimes take a good while for your delete action to be sent to the TweetDeck servers, so if you restart too soon it will never be sent and the column will remain.

TweetDeck keyboard shortcurts

TweetDeck provides an ever-increasing set of keyboard shortcuts. In TweetDeck, press ? to see an up to date list.

Global actions

These work from anywhere.

A Add column
S Search
N New tweet
CTRL/CMD+return Send tweet (compose/reply/dm)
ESC Close popup, cancel search
? Show keyboard shortcut list

TweetDeck provides a full set of shortcuts to navigate your columns and tweets.

1-9 Jump to columns 1-9
0 Jump to the last column on the right
Move focus left
Move focus right
Move focus up
Move focus down
If no tweet is selected, any arrow key will select the first tweet. If you press a number key, the column will be selected. Pressing up/down will then select the first tweet in the column.
esc Clear selection

Tweet actions

Once you have selected a tweet with the navigation keys, you can perform the following actions:

Return/Enter Show tweet detail
Backspace/delete Back to column
R Reply
T Retweet
F Favorite
D DM tweet author
P Show profile for tweet author