Facebook Open Graph sharing

Leafdude says, "Welcome to the FB OG page!"

Note: Some of these screenshots are from our test application (called fbkhantest).
OG = Open Graph. KA = Khan Academy. FB = Facebook.

1. Authentication dialog (same as before)

2. Earning a badge

When you earn a badge, the badge notification appears:
When you hover over the Facebook link and click,

The following permissions dialog pops up, asking you to grant the permission.

If you miss the opportunity to share in the badge notification view, you can also share from the Recent Completed Activity list in your profile:
The share links are visible when you hover over a badge you've earned.
 After trying to share the same badge, you get the following notice: "This badge has already been posted to your timeline".

3. Timeline aggregations

When publishing OG actions ("earn badge") the user will automagically get aggregations in their Timeline showcasing their badges. They can edit the badges that appear in their aggregation, delete posts, or disable the app completely.
User clicks on the edit icon > View Individual Stories.

They are brought to this view:

They can select display settings for each of the badges.

4. Newsfeed aggregations

If a friend of a user earns lots of badges and the FB Newsfeed algorithm deems this worthy of the Newsfeed, aggregate views may appear in the Newsfeed.
OG newsfeed aggregation

What happens when you click "see 3 more":

What happens when you hover over "7 other badges":

What happens when you click on "7 other badges":

Design Decisions (a.k.a., the Long Journey)

1) Use publish_stream, not publish_actions

  • In order to publish custom actions using Open Graph (ex: "Stephanie earned the Telling Time badge on Khan Academy"), users must grant either publish_stream or publish_actions permissions.
  • publish_actions is not an extended permission, so users MUST accept it if they want to log into the site. Users who accept this permission are automatically upgraded to the Facebook Timeline.
  • publish_stream is an "extended" permission, meaning users can decide to not grant it but still log into the site. publish_stream comes with a dialog with room to explain why we are asking for the permissions (publish_actions does not let us explain this within the Facebook dialog).
  • So publish_stream was the clear winner here, as it gives users more flexibility and has a built-in mechanism to allow us to explain our intentions. Too bad we weren't able to find it until we'd resigned ourselves to publish_actions. Sigh, Facebook documentation struggles.

2) Don't ask for permission until the user needs it

  • Initially, we were going to require users to decide whether or not to grant the publish_stream permission at login. They would get the regular log-in dialog, and then, if they haven't granted access, this one right after:
  • However, say a user decides not to grant us access and skips this permission. The next time the user tries to log in via Facebook, we won't know in advance and may keep spamming them with the permission request.
  • We later discovered that it's possible to trigger the extended permissions dialog on its own, by using the same FB.login() call but with different parameters. 
  • So, we could work around the previous problem by first logging in the user in the conventional way (only asking for email), check their permissions and our records of if we've shown the request (as far as I know, Facebook doesn't give us a way to distinguish between revoked permissions and never-granted permissions), and then popping up the extra permissions request. 
  • However, we realized it makes more sense to ask for this request in context. The only reason we need this permission is to allow users to share badges, and, in the future, other activity on the site. So instead of asking every single user upfront, why not ask them when they have already taken steps to share their activity on Facebook (clicking the "share" button on a badge dialog)? That's a much clearer signal of intent.
  • So, we decided to go along with this.

3) Dancing around pop-up blockers

  • Upon a user click on "share", we thought to first ask Facebook for user permissions, then based on the response, attempt to post an Open Graph share (if publish_actions was granted) or prompt the user to grant us extended permissions if not. If after that they still failed to give us the desired permissions, we'd fall back to a standard Feed share.
  • However, most browsers have this rule whereby a pop-up dialog (in this case, the extended permissions/login dialog) is blocked unless it is directly caused by a user click event. In this case, the user click is associated with the asynchronous request to Facebook to detect user permissions. When we trigger another asynchronous request to Facebook to prompt the extended permissions dialog, the click handler has finished executing. Thus, this second request is not associated with a click, and, it's blocked. Dangit!
  • So, we decided to go along with a different approach that makes sure the pop-up request for permission gets triggered directly by the click event handler.
  • [TODO: describe approach??]

4) What we don't know can't hurt us... or can it?

    • In order to save requests to Facebook asking for user permissions (such requests are slow), we would like to store them in a persistent way, so that we retain knowledge of a user's permission status even when they've closed the tab/window, navigated away, or logged out of the site. We first investigated using cookies, because localStorage is not supported by all browsers. We then found out that we can choose to only focus on browsers that support localStorage (yay).
    • Even though we can store such information, because Facebook allows users to revoke app permissions from within the Facebook website, our variables may not always be up-to-date. Hence, we can run into the following four scenarios:

    The truthThe truth
    User granted
    User never granted permission (default case) or revoked it
    What we thinkUser granted permission1. Post succeeds without a hitch.2. [FALSE POSITIVE] We attempt to post but it fails. User gets the message: “Sorry, you must grant access in order to share this on Facebook. Try again.” If they click again, they get the result of scenario 4.
    What we thinkUser never granted permission (default case) or revoked it3. [FALSE NEGATIVE] The Facebook permission screen pops up and instantly shrinks away (before you can see anything), because permissions were actually already granted. It's similar to what you see when you log into KA with FB and are already logged into FB.4. User is prompted to grant permission. If they still deny permission, they get the message “Sorry, you must grant access in order to share this on Facebook. Try again.”

    5) "Picking Up Steam" and other such badges can only be shared once

    • Although users can earn "Picking Up Steam" in any exercise, we decided to count it as a single object on Facebook Open Graph. We figure it's not as interesting for a user to have 4 identical-looking "Picking Up Steam" badges in their Timeline showcase. If we add the context (e.g., "Picking Up Steam in Addition 1"), the badge names get exorbitantly long and cumbersome.

    6) Users can share badges on Facebook even without a KA account

    • Users are allowed to share badges on Facebook without a KA account.
    • PROS: 
      • Easier to implement.
    • CONS:
      • Our Facebook app user base will no longer be a direct subset of the KA user base. Also, if a user who has approved our Facebook app (required in order to share the "earn" action) later signs up for a KA account with their Gmail address, and the Gmail address is different from the (primary?) email of their Facebook account, the accounts will not be automatically linked, which may cause annoyance to some users. However, those users are already in a pickle with the current system, so the increase in inconvenience is not huge.
      • Our badge counts may be off, since users can post earnings of badges on Facebook that aren't recorded in our database. However, the badges shared through this mechanism are fairly easy-to-get ones (Thumbs Up, Picking Up Steam).

    7) Only support Open Graph, not traditional "share"