in Профессиональное

Инвайт-диалог стандартными средствами facebook

Для facebook приложений есть много полезных виральных фич, которые позволяют быстро наращивать пользовательскую базу вашего приложения. Одной из таких фич является форма приглашения друзей (invite). Изначально она формируется при помощи FBML тагов и выглядит вполне себе круто и функционально. Но у данной формы есть один недостаток – для того чтобы перейти к ней, пользователь должен уйти с основной странички приложения (особенно если это flash/flex приложение, при возврате придется его перезагрузить, переинициализировать и т.п.).

Ниже приводится простой способ отобразить диалог с формой инвайта, который не будет обновлять страницу при сабмите или отказе от него.

Первым шагом необходимо подготовить для отображения форму инвайта:

<fb:js-string var="dialog_fbml">
<div id="dialog-content" style="">
    <fb:request-form
        method="POST"
        invite="true"
        type="sample network"
        content="Blah blah blah <fb:req-choice url='http://www.facebook.com/' label='Check out this network!' />"
    >
    <fb:multi-friend-selector
        showborder="false"
        actiontext="Invite your friends to this network."
        max="4"
        rows="2"
        cols="2"
    />
    </fb:request-form>
</div>
</fb:js-string>

Пару слов о том что мы тут делаем: fb:js-string необходим для того чтобы мы могли отобразить диалог с формой – иначе FBJS не умеет. Данная строка (т.е. наша форма) будет при отображении страницы фейсбуком помещена в переменную dialog_fbml, которая в последствии будет использоваться при отображении диалога. Сама форма составлена в общем-то по эталону из документации (подробнее можно прочесть в документации fb:request-form).

Двигаемся дальше. Форму нам надо отобразить. Для этого нам нужна JS функция. И еще одна для сабмита формы:

var inviteDialog;
function showInviteDialog(title) {
    inviteDialog = new Dialog();
    inviteDialog.showChoice(title, dialog_fbml);
}
function inviteAction(){
    inviteDialog.hide();
}

Тут тоже все просто. Зачем и как мы будем использовать inviteAction расскажу чуть позже, а пока разместим на канвасе кнопку, которая будет отображать наш диалог:

<input
    id      = "pop-dialog-shower"
    type    = "button"
    value   = "Invite!"
    onclick = "showInviteDialog('Invite!');"
/>

Ок. Давайте взглянем как это выглядит (диалог показать мы уже можем):

Это уже что-то! Однако есть два проблемных участка, выделенных маркером и пронумерованных:

1. Форма инвайта неприлично больше диалога и выезжает за его границу. Это будем решать при помощи строенных стилей, которые перекроют оригинальные фейсбуковские. Мы хотим расширить окно диалога до размера формы. Добавим в нашу страницу тег <style> и следующий стиль для нее:

.generic_dialog_popup {
    width:650px;
}

2. Стандартные кнопки диалога. К сожалению диалог без кнопок создать нельзя, поэтому мешающие нам кнопки мы попросту спрячем. Для этого добавим на страницу такой стиль:

.contextual_dialog .dialog_buttons .inputbutton,.inputsubmit {
    display:none;
}

Загружаем изменения на сервер, смотрим и радуемся. Теперь форма и диалог производят впечатление единого целого.

Но! Если мы отправим приглашение или даже откажемся от него (какое кощунство )))) то страница все-равно перегрузится. Добивались мы совсем не этого, посему нам остался заключительный финт ушами, в котором нам поможет функция inviteAction. Но перед тем как я продемонстрирую код, который нам нужно написать, пара комментариев о том как это будет работать:

  • Во-первых, для достижения необходимого эффекта (инвайта без перезагрузки страницы) нам нужно воспользоваться одной мало используемой возможностью форм – а именно, возможностью указывать в качестве action – javascript функцию (аналогично это работает в href ссылок). Если честно, пробовал я это наугад, проверил в основных браузерах – и это работало. При этом, facebook не предоставляет по большому счету никаких возможностей по управлению поведением формы инвайта, за исключением action и method. Собственно для этих целей и определена функция inviteAction.
  • Во-вторых, если мы напрямую укажем inviteAction в качестве экшена формы, то при попытке сабмита или отказа мы увидим ошибку что такой функции нет. Происходит это потому, что facebook при рендеринге страницы парсит весь встроенный JS код и подменяет имена функций и переменных, используя app_id. При этом, видимо, не ожидается что внутри fb:request-form будет использоваться функция и соответственно подмена имени не происходит. Поэтому не поленитесь узнать свой app_id на страничке разработчиков и модифицируйте имя функции по правилу a[app_id]_inviteAction(). Например a123456_inviteAction().

Итак, у нас был FBML таг формы запроса:

    <fb:request-form
        method="POST"
        invite="true"
        type="sample network"
        content="Blah blah blah <fb:req-choice url='http://www.facebook.com/' label='Check out this network!' />"
    >

Для достижения нашей цели мы должны добавить параметр action оговоренного ранее вида (app_id конечно же выдуманный, поэтому не забывайте его заменять на актуальный для вашего приложения):

    <fb:request-form
        method="POST"
        invite="true"
        action="javascript:a123456_inviteAction()"
        type="sample network"
        content="Blah blah blah <fb:req-choice url='http://www.facebook.com/' label='Check out this network!' />"
    >

Обновите код – и наслаждайтесь )

Итак, эта форма имеет следующие достоинства:

  • Инвайт без перезагрузки страницы.
  • Удобство для пользователя.
  • Фокус пользователя не рассеивается на перемещение по страницам.
  • Быстро отображается (т.к. загружена заранее).

А также недостатки:

  • При передаче традиционным способом еще передается список ids – кому отправлен инвайт. В этом случае он теряется (во всяком случае я пока не вижу способа как его можно получить – если подскажете, будет вообще здорово ))).
  • Это все один сплошной хак ))) сам facebook такого функционала не предоставляет.

И еще один момент, который ни достоинство ни недостаток:

  • Т.к. action поидее должен использоваться для редиректов и некоторых последействий, то, чтобы не терять такой возможности, можно вызвать нужный URL через Ajax.post внутри функции inviteAction.

Меня это соотношение устраивает. Если у вас есть какие-то замечания и комментарии – можете оставлять их ниже.

P.S. на самом деле видов диалогов в facebook – два, POP, как в данном примере и CONTEXTUAL – который можно привязать к контексту (например какому-то элементу по ID). Второй тип диалога мне нравился больше, но увы, форма инвайта в нем не работает. По этому поводу я засабмитил баг 7894, но не уверен в том что в ближайшем будущем у кого-то до него дойдут руки.

P.P.S. Source code for this article (how to place facebook invite in standard dialog) you can download by clicking here.

Write a Comment

Comment

*

20 Comments

  1. Огромное спасибо! Сэкономил кучу времени, т.к. как раз возникла необходимость реализовать данный функционал.

  2. Hello.
    Thanks a lot for the code it works perfectly just 1 question. When you call press the button and don’t select any friend it gives a warning that no user is selected. If you press OK on this new dialog you are not returned to the previous one. Do you know how to catch the event on OKEY pressed on the new dialog, so to call again inviteDialog.showChoice(title, dialog_fbml);

    Thanks in advance.

  3. Продолжаем тему хакинга кодов.
    У меня получилось без проблем делать инвайты. Но на этом я не остановился. Задача стояла посылать реквесты (подарки) друзьям. Возникла проблема:
    1. Как динамически создать окно реквеста без перезагрузки.
    2. Как поймать ай-ди юзеров, которым отсылаем подарок.
    Первую проблему решил за день, используя не хитрый код на аякс.
    Вот линк примера: http://wiki.developers.facebook.com/index.php/FBJS/Examples/Dialogs/Ajax
    Вторая проблема решена частично.
    А именно:
    1) создал на той же странице айфрейм невидимый типа:
    2) в акшинах прописал вместо джаваскрипт ф-ии следующее:
    …….
    <fb:request-form
    method="POST"
    invite="false"
    action="http://ваш.сервер.ком/demo/send.php&quot;
    target="act"
    type="sample network"
    …….
    При сабмите шлется все класно в сенд.пхп, но окно не исчезает автоматом. Пока пришлось возвратить кнопки ОК и Кансел стандартного окна.
    Может кто решит проблему быстрее меня, так черкните сюда плиз.

  4. Продолжаем тему хакинга кодов. У меня получилось без проблем делать инвайты. Но на этом я не остановился. Задача стояла посылать реквесты (подарки) друзьям. Возникла проблема: 1. Как динамически создать окно реквеста без перезагрузки. 2. Как поймать ай-ди юзеров, которым отсылаем подарок. Первую проблему решил за день, используя не хитрый код на аякс. Вот линк примера: http://wiki.developers.facebook.com/index.php/FBJS/Examples/Dialogs/Ajax Вторая проблема решена частично. А именно: 1) создал на той же странице айфрейм невидимый типа: 2) в акшинах прописал вместо джаваскрипт ф-ии следующее: …….

    • <fb:request-form
      method="POST"
      invite="false"
      action="http://server.com/demo/send.php&quot;
      target="act"
      type="sample network"
      ………….
      Проблема: как сделать прятанье диалога теперь? 🙂
      Пока вывожу только кнопки ОК и отмена.
      Черкните, если кто решит эту траблу.

      • У меня он как раз прячется в JS функции. Другого способа не вижу. А с реквестами советовал бы не заморачиваться сильно, они скоро отомрут. По крайней мере разработчики facebook обещали.

  5. У меня ошибка:

    Dialog is not defined
    [Break on this error] inviteDialog = new Dialog();

    Что за Dialog ?

  6. Что делать с ошибкой:
    Dialog is not defined
    [Break on this error] inviteDialog = new Dialog();

    • Ну как бы дайте немного больше подробностей. Есть у меня подозрение, что у вас IFRAME-приложение.

      Суть ошибки в следующем – на странице не подключен javascript с определением Dialog (ну или более редкая ошибка когда в названии встречается русская буква).

      А вот почему у вас нет определения этого объекта, я по указанным данным не могу определить.

  7. Сорри за краткость.
    Вот что мы имеем:

    var inviteDialog;
    function showInviteDialog(title) {
    inviteDialog = new Dialog();
    inviteDialog.showChoice(title, dialog_fbml);
    }
    function inviteAction(){
    inviteDialog.hide();
    }

    <fb:request-form
    method="POST"
    action="javascript:a[replace_me_to_your_app_id]_inviteAction()"
    invite="true"
    type="sample network"
    content="Blah blah blah ”
    >

    FB.init(“MY_KEY”);

    function initFB() {
    FB_RequireFeatures([“XFBML”], function(){
    FB.init(“MY_KEY”, “xd_receiver.htm”);
    });}

    • Ну насколько я вижу – это мой пример – и у меня он работал )) Давайте наверное наберите меня в скайпе (dmitry.bykadorov) – тут в комментариях мы правды не сыщем.

    • In IFRAME you can use many other solutions i think (e.g. jQuery).

      And unfortunately, the Facebook team fixed this workarround, so it doesn’t work in FBML also (( Sorry…

  8. Здравствуйте, я только начинаю разрабатывать приложения для facebook, есть некоторые проблемы с JavaScript’ом не могу найти толкового мануала по FBJS, даже на сайте facebook’а примеров не так много, и совсем не могу найти информацию по анимации, может подскажете где можно пополнить свои знания по этому вопросу?

    • К сожалению кроме документации Facebook основной источник знаний – собственный опыт (ну и читать форум facebook чтобы по возможности не наступать на грабли самому а учиться у других). Кроме того я бы рекомендовал не смотреть уже в сторону FBML/FBJS а переходить сразу на IFRAME/JS SDK.

  9. Подскажите юному работнику над Facebook интеграцией с чем может быть связано отображение лишь кнопки “Invite!”, при полном копировании кода из примера?

    • Увы, Фейсбук развивается и данный хак закрыли примерно этим летом.