Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 16720 сообщений
Cообщения с меткой

вКонтакте - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

[Из песочницы] API для управления плеерами на сайтах

Среда, 20 Июля 2016 г. 19:47 (ссылка)

Предыстория



В 2012-2013 годах я нашёл одно онлайн-радио, которое «зацепило» меня большим выбором хорошей музыки и тем, что там (почти) не было рекламы.



По своей должности я — web-разработчик, да ещё интересуюсь различными технологиями и происходящим в науке, и посему у меня довольно часто открыто большое количество вкладок в браузере, а зачастую ещё и несколько профилей в браузере. А если ещё добавить к этому среду разработки, графический редактор, открытые папки, то хоть заводи навигатор по всем открытым окнам и вкладкам.



Музыка во вкладке в браузере у меня играла большую часть времени. Когда мне нужно было приостановить воспроизведение, чтобы ответить на звонок или посмотреть интересный видеоролик, приходилось сначала найти нужное окно браузера, потом — нужную вкладку и только после этого можно было нажать заветную кнопку паузы.



Такое происходило часто, и я всё чаще стал задаваться мыслью найти решение этой проблемы.

Недолго думая, решил написать расширение для браузера, которое позволяло бы с помощью горячих клавиш (в терминологии некоторых браузеров — быстрые клавиши) контролировать воспроизведение.



А ещё часто мне было интересно узнать исполнителя и название произведения, поэтому было решено добавить всплывающие оповещения с этой информацией.



Готовое расширение



Сегодня моё расширение позволяет контролировать воспроизведение уже на нескольких сайтах (SoundCloud, Digitally Imported, ВКонтакте, Одноклассники, Онлайн-радио 101.ru, VGM Radio), а также некоторые другие действия (добавлять текущий трек в плейлист, отмечать трек как понравившийся, изменять громкость звука). Делается это как с помощью быстрых клавиш, так и при помощи кнопок в оповещении (кнопки не отображаются в Opera).



Ещё есть (отключаемые) оповещения с информацией о треке при изменении статуса плеера либо по нажатию быстрых клавиш.



Также ведётся список десяти последних треков с возможностью поиска оных ВКонтакте, в Google и Amazon.



API



У расширения имеется API для добавления поддержки других сайтов.



Каждый сайт (плеер) добавляется как модуль. Имеются встроенные и внешние модули.



Встроенные модули


Встроенные модули являются частью моего расширения и их настройки доступны на странице параметров расширения. Для того, чтобы подключить модуль, пользователь должен дать расширению своё разрешение на доступ к вкладкам и содержанию страниц соответствующих доменов.



Внешние модули


Внешние модули являются отдельными расширениями, которые подсоединяются к моему расширению посредством API и имеют доступ ко всем возможностям моего расширения (управление плеерами посредством быстрых клавиш и кнопок в оповещении, а также список последних треков). Их настройки тоже доступны на странице параметров моего расширения.



Для чего нужны внешние модули



  • Некоторые модули могут требовать доступ ко всем доменам (например, модуль для виджета SoundCloud на любых сайтах), и чтобы не пугать пользователя моего расширения запросом такого разрешения (чтобы сохранить доверие пользователя).

    Такие модули могут получать это разрешение сразу при установке либо в любое другое время (на усмотрение разработчика).

  • Внешние модули позволяют сторонним разработчикам публиковать расширения под своим именем. Разработчики, которые хотят пополнить своё портфолио, вместо того, чтобы участвовать в чужом проекте, создают свой собственный проект.



Где найти API и как с ним работать


API доступен по лицензии MIT на GitHub (см. Ссылки ниже).



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



Примеры использования API


Имеется: два внешних модуля (доступны в качестве отдельных расширений), а также один встроенный модуль, использующий API.



Встроенный или внешний модуль


Встроенный или внешний модуль? «… вот в чём вопрос», — как говорил классик.



Если вы хотите воспользоваться API и добавить поддержку нового плеера, воспользуйтесь следующими подсказками:




  • встроенный модуль:

    — ваш модуль поддерживает плеер только на одном или небольшом количестве доменов (например, модуль для youtube.com, модуль для play.google.com/music/listen);

    — вас устроит упоминание о вашем вкладе на странице readme моего расширения и в социальных сетях;

  • внешний модуль:

    — вы хотите опубликовать свой модуль под своим именем;

    — ваш модуль требует доступ к содержанию страниц на большом количестве или всех доменах.



Ссылки





Обратная связь



Вопросы, жалобы и предложения приветствуются. Оставляйте их в комментариях либо воспользуйтесь одним из других способов.


Original source: habrahabr.ru.

https://habrahabr.ru/post/306072/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Комментарии (0)КомментироватьВ цитатник или сообщество
lj_dolboeb

Приговор инженеру Бубееву: 2 года и 3 месяца колонии-поселения за репосты во ВКонтакте

Среда, 20 Июля 2016 г. 15:05 (ссылка)

Тверской областной суд несколько минут назад рассмотрел апелляцию инженера Андрея Бубеева на приговор, вынесенный ему Заволжским райсудом за серию репостов в социальной сети ВКонтакте.

Приговор оставлен без изменения: Бубеев по-прежнему осуждён на 2 года и 3 месяца в колонии-поселении. С 24 мая 2015 года он находится в СИЗО, и сегодняшний приговор тоже не означает выхода из тюрьмы: областной суд постановил доставить «преступника» прямиком из следственного изолятора в колонию под конвоем. Покуда ФСИН разбирается, куда его везти, Андрей Бубеев будет сидеть в тюрьме.

Начало заседания Тверского облсуда задержалось на три с половиной часа, зато с вердиктом в 15 минут удачно уложились. И в самом деле, что зря голову ломать.

http://dolboeb.livejournal.com/3010940.html

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
Фитиль_Дом2 (Автор -Лариса_Дунаева)

«ВКонтакте» залезет в карман к своим пользователям.

Среда, 20 Июля 2016 г. 10:47 (ссылка)

В известной социальной сети «ВКонтакте» введут оплату за бесплатные ранее функции. Также будут переосмыслены некоторые разделы, рассказывают «Известия».
1401017624 (600x340, 107Kb)

Изменения коснутся музыки на сайте. Прослушивать треки пользователи смогут, как и раньше, свободно. Платить придётся за добавление песни к себе в аудиозаписи.

Читать далее...
Метки:   Комментарии (3)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Делаем крутые Single Page Application на basis.js — часть 3. Клиент для ВКонтакте

Воскресенье, 17 Июля 2016 г. 14:39 (ссылка)



Всем доброго времени суток.

Продолжаю интереснейший цикл статей про создание продвинутых Single Page Application на basis.js.

В прошлый раз мы научились работать с коллекциями и реализовали полноценный интерактивный список.

В этот раз мы начнем создавать полноценный клиент для ВКонтакте.

А именно: реализуем авторизацию, загрузку новостей, друзей и музыки.



Меню и навигация



Для начала, реализуем простую страничку с заголовком и меню.



При клике по пунктам меню (вкладкам), происходит навигация на разные url и выделение активного пункта меню.

Давайте посмотрим на код главного файла нашего будущего приложения:

let Node = require('basis.ui').Node;
let Header = require('app.ui.header.component');
let Menu = require('app.ui.menu.component');

require('basis.app').create({
title: 'VK Client by Basis.JS',
element: new Node({
template: resource('./template.tmpl'),
binding: {
header: 'satellite:',
menu: 'satellite:'
},
satellite: {
header: Header,
menu: Menu
}
})
});


Подключаем необходимые модули: Node, а так же компоненты заголовка и меню (которые рассмотрим ниже).

Далее создается приложение, при помощи метода basis.app.create(). Можно, конечно, обойтись без него и делать так, как мы делали раньше — создавать новый Node и помещать его в какой-нибудь элемент на странице.

Но в basis.js имеется хелпер basis.app, который инкапсулирует некоторую логику, связанную с заголовком страницы и размещением корневого компонента приложения на странице.

Так же, посмотрим на шаблон нашего приложения:









Заголовок и меню являются сателлитами корневого компонента нашего приложения.

На данный момент, компонент заголовка очень простой:

let Node = require('basis.ui').Node;

module.exports = Node.subclass({
template: resource('./template.tmpl') //

Добро пожаловать!


});


Его задача — выводить приветствие. Позже мы его улучшим.

А вот компонент меню представляет для нас особый интерес:

let Node = require('basis.ui').Node;
let Value = require('basis.data').Value;
let router = require('basis.router');
let currentPage = Value.from(router.route(':page').param('page'));

module.exports = Node.subclass({
template: resource('./template.tmpl'), //

childClass: {
template: resource('./item.tmpl'),
selected: routePage.compute((node, page) => node.url == page),
binding: {
title: 'title'
},
action: {
click() {
router.navigate(this.url);
}
}
},
childNodes: [
{title: 'Новости', url: 'news'},
{title: 'Друзья', url: 'friends'},
{title: 'Музыка', url: 'audio'}
]
});


Давайте сразу обратим внимание на содержимое переменной currentPage.

Здесь всегда будет храниться актуальный маршрут, изменения которого мы можем отслеживать.

Это значение мы используем в свойстве selected пунктов меню.

То есть активность конкретного пункта меню зависит от текущего маршрута.

Если url текущего пункта меню совпадает с текущим маршрутом, то у этого пункта меню свойство selected = true.

Таким образом, в один момент времени будет выбран только один пункт меню.

При клике на конкретный пункт, происходит навигацию к указанному url.

Более подробно о роутере, встроенном в basis.js можно почитать в соответствующем разделе документации.



Теперь посмотрим на шаблон пункта меню:






Каждый пункт меню — это кнопка. Если selected пункта равен true, то добавляем к кнопке класс active, в ином случае — убираем.



Вот и всё. Меню с навигацией — готово.

Теперь, при нажатии на пункты меню, будет происходить переход по соответствующему url.

Осталась небольшая мелочь — маршрут по умолчанию.

Если просто открыть наше приложение, без указания маршрута, то ни один пункт меню выбран не будет.

Давайте исправим это таким образом, чтобы маршрутом по умолчанию были Новости.

Модифицируем основной файл нашего приложения:

// ...
let router = require('basis.router');
let defaultRoute = 'news';

require('basis.app').create({
title: 'VK Client by Basis.JS',
element: new Node({
// ...
})
}).ready(() => {
router.route('*page').param('page').as(page => page || router.navigate(defaultRoute));
});


Как только приложение будет проинициализировано, начинаем отслеживать изменение маршрута.

Если маршрут не указан, то перебрасываем пользователя на маршрут по умолчанию.



Авторизация



Теперь задействуем ВКонтакте API и реализуем с его помощью авторизацию.

Посмотрите на обертку над VK API (далее просто API). Мы не будем рассматривать ее полностью, а посмотрим только на ключевые моменты.

Обратите внимание, что сам API является наследником от basis.data.Value.

Это значит, что у нее, как и у любого источника данных, есть состояния:


  • UNDEFINED когда пользователь не авторизован

  • PROCESSING во время авторизации

  • READY после успешной авторизации

  • ERROR в случае ошибки



Посмотрим на то, как реализована смена состояний модели. Для этого обратимся к методам login() и logout():

login() {
this.setState(STATE.PROCESSING);
this.isLoggedIn().then(
() => this.setState(STATE.READY),
() => {
global.VK.Auth.login(response => {
this.setState(response.session ? STATE.READY : STATE.UNDEFINED);
}, config.perms);
}
);
},
logout() {
global.VK.Auth.logout();
this.setState(STATE.UNDEFINED);
}


Вызывая login(), API будет переведен в состояние PROCESSING.

Далее идет проверка — если пользователь уже авторизован, то сразу переводим API в состояние READY. Если нет, то авторизуемся при помощи метода VK.Auth.login() из VK API. Процесс авторизации через VK API сводится к тому, что вам показывается окно с предложением ввести логин и пароль.



Когда окно будет закрыто (авторизация прошла успешно или была отменена), будет вызван переданный callback, в котором будет установлено конечное состояние нашей модели: READY, в случае успешной авторизации и UNDEFINED, в случае отмены авторизации.

Вызывая logout(), уничтожаем сессию методом VK.Auth.logout() и переводим API в состояние UNDEFINED.

Теперь посмотрим на другой важный метод — callApi():

callApi(method, params = {}) {
return this.isLoggedIn()
.catch(e => Promise.reject(new Error('Ошибка авторизации!')))
.then(
() => {
return new Promise((resolve, reject) => {
basis.object.complete(params, {v: config.version});

global.VK.api(method, params, response => {
if (response.error) {
reject(new Error(response.error.error_msg));
} else {
resolve(response.response);
}
});
});
},
e => {
this.setState(STATE.ERROR, e.message);
throw e;
}
);
}


Суть данного метода — отправить запрос через VK API. Перед выполнением каждого запрос проверяем наличие авторизации. Если авторизации нет (например мы открыли наше приложение в двух вкладках браузера и в одной из них нажали выйти), то выбрасываем ошибку и переводим API в состояние ERROR. Если с авторизацией всё хорошо, то выполняем запрос. Если сервер, в ответ на запрос, сообщает нам об ошибке — выбрасываем ошибку и переводим API в состояние ERROR. В ином случае — возвращаем результат.



За счет этого, мы можем абстрагироваться от нюансов работы с VK API и оперировать лишь состояниями модели:

let STATE = require('basis.data').STATE;
let Value = require('basis.data').Value;
let Node = require('basis.ui').Node;
let router = require('basis.router');
let Header = require('app.ui.header.component');
let Menu = require('app.ui.menu.component');

let vkApi = require('app.vkApi');
let apiState = Value.state(vkApi);
let defaultRoute = 'news';

require('basis.app').create({
title: 'VK Client by Basis.JS',
element: new Node({
// компонент приложения активен, пока пользователь авторизован
active: apiState.as(state => state == STATE.READY),
// компонент приложения заблокирован в процессе авторизации
disabled: apiState.as(state => state == STATE.PROCESSING),
template: resource('./template.tmpl'),
binding: {
header: 'satellite:',
menu: 'satellite:',
// будет содержать текст ошибки, в случае ее возникновения
error: apiState.as(state => state == STATE.ERROR && state.data)
},
satellite: {
header: Header,
menu: Menu
},
action: {
// обработка кнопки "авторизоваться"
login() {
vkApi.login();
}
}
})
}).ready(() => {
router.route('*page').param('page').as(page => page || router.navigate(defaultRoute));
// пытаемся авторизоваться сразу после инициализации приложения
vkApi.login();
});


Теперь применим эти свойства в шаблоне:










VK Client
powered by basis.js



{error}






Показываем экран приветствия пока пользователь не авторизован, в ином случае — показываем меню и заголовок.

Кнопка Авторизация будет заблокирована в процессе авторизации.

Так же, добавим кнопку выйти в главное меню:







И, в компоненте меню, обработаем клик по этой кнопке:

let vkApi = require('app.vkApi');
// ...

module.exports = Node.subclass({
// ...
action: {
logout() {
vkApi.logout();
}
}
// ...
});




Отлично! Теперь у нас есть приложение, которое может авторизоваться в ВКонтакте, а так же удобный механизм отслеживания состояний модели. Двигаемся дальше.



Старницы



В рамках этой статьи мы реализуем три страницы: новости, друзья и аудиозаписи.

При переключении между пунктами меню, должна отображаться соответствующая страница.



Для начала, создадим общую страницу, от которой будем наследовать все остальные:

let Value = require('basis.data').Value;
let Expression = require('basis.data.value').Expression;
let STATE = require('basis.data').STATE;
let Node = require('basis.ui').Node;

module.exports = Node.subclass({
active: basis.PROXY,
binding: {
loading: Value.query('childNodesState').as(state => state == STATE.PROCESSING),
error: Value.query('childNodesState').as(state => state == STATE.ERROR && state.data),
empty: node => new Expression(
Value.query(node, 'childNodesState'),
Value.query(node, 'childNodes.length'),
(state, itemCount) => !itemCount && state == STATE.READY
)
},
handler: {
activeChanged() {
if (this.active) {
this.dataSource.deprecate();
}
}
}
});





загружается...

список пуст

{error}





Так уж получилось, что все три страницы имеют общую логику работы:


  • загрузить список чего-либо

  • при загрузке показывать надпись загружается

  • в случае ошибки вывести текст ошибки

  • если был загружен пустой список, то показывать надпись список пуст



Нечто похожее мы уже делали в прошлый раз.

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

Давайте подробнее рассмотрим что там происходит:

Наша абстрактная страница — это всего лишь Node с определенными биндингами и еще парой деталей.

Я не буду сейчас останавливаться на этих биндингах, так как они подробно разбирались в прошлый раз.

Сейчас нас больше инеересует другое.

Что такое active: basis.PROXY?

В прошлый раз мы выяснили, что набор данных начинает синхронизацию только когда его состояние UNDEFINED или DEPRECATED и у него есть активный потребитель. Выполнение этих двух условий обязательно для того, чтобы запустить процесс синхронизации набора. Сейчас нас больше интересует часть про "когда есть активный потребитель".

Потребитель — это сущность (наследник basis.data.AbstractData), которой нужны данные (и при том актуальные), представленные в другом объекте данных.

Активный потребитель — это потребитель со свойством active = true.



По умолчанию, когда для Node назначается dataSource, Node автоматически становится потребителем данного набора.

Отлично, потребитель есть. Но активный ли он?

Опять же, по умолчанию, Node не является активным потребитетем (свойство active = false).

"А давайте просто добавим active: true в описание Node и проблема будет решена" — можете предложить вы.

Не всё так однозначно. Мы ведь делаем умное приложение? А значит набор должен не просто один раз синхронизироваться при запуске приложения, а еще и обновлять свои данные по мере необходимости.

У нас есть три страницы и три набора под каждую из них (новости, друзья и аудиозаписи). Будем запускать синхронизацию набора только тогда, когда переходим на вкладку, которая нуждается в этом наборе. Таким образом, мы не только реализуем механизм актуализации данных, но еще и добавим "ленивую" синхронизацию данных. То есть синхронизацию только при необходимости.

Исходя из этого, при переходе на какую-либо вкладку, мы должны переводить состояние соответствующего набора в DEPRECATED.

Но как узнать, что мы переключились на какую-либо вкладку?

Скорее всего вы уже начали думать, что мы всё дальше отдаляемся от первоначального вопроса.

Но это не так. Еще чуть-чуть и вы увидите, как все сюжетные линии сольются воедино, прояснив общую картину.

Итак, как узнать, что мы переключились на какую-либо вкладку?

Как и в случае с dataSource, Node автоматически становится потребителем всех своих сателлитов, а так же их владельцем. Мы сделаем так, что при переключении между вкладками, соответствующая страница будет становиться сателлитом корневого Node нашего приложения.

Значит можно заставить страницу среагировать в тот момент, когда она станет сателлитом и в этот момент перевести состояние своего набора в DEPRECATED:

// ...

module.exports = Node.subclass({
// ...
handler: {
ownerChanged() {
if (this.owner) {
this.dataSource.deprecate();
}
}
}
});


Отлично! В тот момент, когда страница станет сателлитом корневого компонента нашего приложения, е набор данных перейдет в состояние DEPRECATED.



Но давайте еще раз проговорим: "Набор данных начинает синхронизацию только когда его состояние UNDEFINED или DEPRECATED и у него есть активный потребитель."

С переключением состояния набора и наличием потребителя мы разобрались. Но как быть с активностью? Если просто добавить к странице active: true, то она будет всегда активна и ее источник данных попытается синхронизировать данные сразу при создании, вне зависимости от того, нужны нам сейчас эти данные или нет.

Нам это не совсем подходит, ведь есть случаи, когда синхронизация просто невозможна. Например, когда мы еще не прошли процедуру авторизации или отключен интернет.

Чтобы не заоморачиваться обработкой этих кейсов в самой странице, добавим ей свойство active: basis.PROXY, которое переведет Node в особый режим, в котором Node будет активен только тогда, когда у него самого есть активный потребитель.

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

Взгляните еще раз на итоговый код компонента страницы:

let Value = require('basis.data').Value;
let Expression = require('basis.data.value').Expression;
let STATE = require('basis.data').STATE;
let Node = require('basis.ui').Node;

module.exports = Node.subclass({
active: basis.PROXY,
binding: {
loading: Value.query('childNodesState').as(state => state == STATE.PROCESSING),
error: Value.query('childNodesState').as(state => state == STATE.ERROR && state.data),
empty: node => new Expression(
Value.query(node, 'childNodesState'),
Value.query(node, 'childNodes.length'),
(state, itemCount) => !itemCount && state == STATE.READY
)
},
handler: {
activeChanged() {
if (this.active) {
this.dataSource.deprecate();
}
}
}
});


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

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

В момент активации страницы, состояние ее источника данных переходит в DEPRECATED.

Так мы делегировали обязанности по обработке различных кейсов основному приложению.



Теперь картина должна проясниться:


  • есть три страницы (новости, друзья, аудиозаписи)

  • у каждой старницы свой собственный набор данных, который умеет получать данные при помощи VK API

  • при переключении между вкладками, сателлитом основного компонента приложения будет становиться соответствующая вкладке страница

  • страница активна только когда у нее есть активный потребитель





Теперь перейдем к созданию наследников рассмотренного компонента страницы.

Начнем с новостей:

let Page = require('../Page');

module.exports = new Page({
template: resource('./list.tmpl'),
childClass: {
template: resource('./item.tmpl'),
binding: {
text: 'data:',
date: Value.query('data.date').as(format('%D.%M.%Y %H:%I:%S'))
}
}
});


Шаблон оставляю на ваше усмотрение.

Код остальных двух страниц аналогичен.

У страницы все еще нет источника данных. К этому вопросу мы еще вернемся, а пока посмотрим на то, как отображать страницу, которая соответствует открытой вкладке.

Модифицируем главный файл нашего приложения:

// ...
let pageByName = {
news: resource('./ui/pages/news/component.js'),
friends: resource('./ui/pages/friends/component.js'),
audio: resource('./ui/pages/audio/component.js')
};

require('basis.app').create({
title: 'VK Client by Basis.JS',
element: new Node({
// ...
binding: {
// ...
page: 'satellite:'
},
satellite: {
// ...
page: router.route(':page').param('page').as(page => pageByName[page])
}
})
})
// ...


Сателлитом с именем page будет являться компонент, который соответствует текущему маршруту на основании карты в переменной pageByName. Теперь нужно добавить использование этого сателлита в шаблон:









...



Теперь, если бы у страниц был источник данных, то наше приложение начало бы работать.



Источник данных



Выше была показана обертка над VK API. Помимо прочего, там есть методы для получения списка новостей, друзей и аудиозаписей. В качестве источника данных, будем использовать basis.entity — типизированные сущности.

Опишем тип для новостей:

let STATE = require('basis.data').STATE;
let entity = require('basis.entity');
let vkApi = require('app.vkApi');

let News = entity.createType('News', {
text: String,
date: Date
});

News.extendReader(data => data.date *= 1000);
News.all.setSyncAction(() => vkApi.news().then(News.all.set));

module.exports = News;


Каждая новость состоит из двух полей — текста и даты.

Заметьте, что мы расширяем reader. Данная возможность используется в тех случаях, когда необходимо модифицировать данные перед тем, как они станут экземпляром типа.

Так же, у каждого типа есть свойство all, которое является набором всех созданных объектов данного типа.

Где бы мы ни создали экземпляр типа News, он будет помещен в набор News.all.

Для данного набора мы определяем syncAction, то есть метод, который будет вызываться в случае необходимости синхронизации.

Всё что нам нужно сделать — получить данные из ВКонтакте и передать их методу News.all.set(), который заменит существующие экземпляры типа News на новые.

Заметьте, что нет необходимости явно указывать контекст метода таким образом: News.all.set.bind(News.all).

Данный метод уже имеет привязку к контексту News.all для удобства использования.

Так же заметьте, что, если метод, указанный в syncAction возвращает промис, то состояние набора данных будет определяться автоматически, в зависимости от состояния промиса.



Теперь News.all может быть передан в качестве источника данных для страницы новостей. Соответственно, в момент активации страницы, состояние News.all будет переведено в DEPRECATED и начнется процесс синхронизации, описанный в syncAction набора



News.all.

Подобным образом опишем оставшиеся два типа:

Friends entity


let entity = require('basis.entity');
let vkApi = require('app.vkApi');
let createAction = require('./promiseAction');

let Friends = entity.createType('Friends', {
photo: String,
first_name: String,
last_name: String
});

Friends.extendReader(data => data.photo = data.photo_100);
Friends.all.setSyncAction(() => vkApi.friends().then(Friends.all.set));

module.exports = Friends;


Audio entity


let entity = require('basis.entity');
let vkApi = require('app.vkApi');
let createAction = require('./promiseAction');

let Audio = entity.createType('Audio', {
artist: String,
title: String,
duration: Date
});

Audio.extendReader(data => data.duration *= 1000);
Audio.all.setSyncAction(() => vkApi.audio().then(Audio.all.set));

module.exports = Audio;


Теперь укажем News.all в качестве источника данных для страницы:

let Value = require('basis.data').Value;
let Page = require('../Page');
let News = require('app.type.news');
let format = require('basis.date').format;
let dataSource = News.all;

module.exports = new Page({
template: resource('./list.tmpl'),
dataSource: dataSource,
childClass: {
template: resource('./item.tmpl'),
binding: {
text: 'data:',
date: Value.query('data.date').as(format('%D.%M.%Y %H:%I:%S'))
}
}
});


Аналогичным образом, укажем соответствующие наборы другим страницам.



Friends page


let Page = require('../Page');
let Friends = require('app.type.friends');
let dataSource = Friends.all;

module.exports = new Page({
template: resource('./list.tmpl'),
dataSource: dataSource,
childClass: {
template: resource('./item.tmpl'),
binding: {
photo: 'data:',
first_name: 'data:',
last_name: 'data:'
}
}
});


Audio page


let Value = require('basis.data').Value;
let Page = require('../Page');
let Audio = require('app.type.audio');
let format = require('basis.date').format;
let dataSource = Audio.all;

module.exports = new Page({
template: resource('./list.tmpl'),
dataSource: dataSource,
childClass: {
template: resource('./item.tmpl'),
binding: {
artist: 'data:',
title: 'data:',
duration: Value.query('data.duration').as(format('%I:%S'))
}
}
});


Разметка либо на ваше усмотрение, либо по ссылке на репозиторий в конце статьи.

Заметьте, что для представления совсем не важен вид разметки, она может быть любой.



Всё готово, но давайте добавим еще немного улучшений.

Добавим страницу 404. Для этого, модифицируем наш главный файл:

// ...
let pageByName = {
news: resource('./ui/pages/news/component.js'),
friends: resource('./ui/pages/friends/component.js'),
audio: resource('./ui/pages/audio/component.js'),
notFound: resource('./ui/pages/404/component.js')
};

require('basis.app').create({
title: 'VK Client by Basis.JS',
element: new Node({
satellite: {
header: Header,
menu: Menu,
page: router.route(':page').param('page').as(page => pageByName[page] || pageByName.notFound)
}
// ...
})
})
// ...


Всё что мы сделали — добавили новый маршрут в карту маршрутов и модифицировали отслеживание изменения маршрута. Если требуемый маршрут не найден в карте маршрутов, то использовать маршрут notFound.



Кстати, вы заметили, что компоненты подключаются через resource а не через require?

resource позволяет реализовать ленивую инициализацию компонентов.

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

Подробнее о ресурсах можно почитать в соответствующем разделе документации.



И еще один момент. Дело в том, что на вашей стене ВКонтакте могут попадаться не только текстовые новости, но еще и видео/фото без текста. Обработкой этих кейсов мы займемся в другой раз, а пока давайте просто отфильтруем новости таким образом, чтобы отображать только те новости, у которых есть текст. Для этого модифицируем компонент новостей:

let Page = require('../Page');
let Value = require('basis.data').Value;
let News = require('app.type.news');
let format = require('basis.date').format;
let Filter = require('basis.data.dataset').Filter;
let textOnlyNews = new Filter({
source: News.all,
state: Value.query('source.state'),
rule: 'data.text',
deprecate() {
this.source.deprecate();
}
});

module.exports = new Page({
template: resource('./list.tmpl'),
dataSource: textOnlyNews,
childClass: {
template: resource('./item.tmpl'),
binding: {
text: 'data:',
date: Value.query('data.date').as(format('%D.%M.%Y %H:%I:%S'))
}
}
});


Всё что мы сделали — подменили источник данных страницы новостей фильтром, который отбросит все новости без текста.



И последнее… оживим компонент заголовка:

let Node = require('basis.ui').Node;
let STATE = require('basis.data').STATE;
let DataObject = require('basis.data').Object;
let vkApi = require('app.vkApi');

let dataSource = new DataObject({
data: {
firstName: '',
lastName: ''
},
syncAction() {
return vkApi.me().then(me => {
this.update({ firstName: me.first_name, lastName: me.last_name });
});
}
});

module.exports = Node.subclass({
active: basis.PROXY,
delegate: dataSource,
template: '

Добро пожаловать {firstName} {lastName}!

',
binding: {
firstName: 'data:',
lastName: 'data:'
}
});


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



Заключение, но не конец



Итак, сегодня мы создали полноценный клиент для ВКонтакте на basis.js. Приложение умеет авторизоваться и синхронизировать данные с сервером.

Обратите внимание на то, что, как и в прошлый раз, мы идем по пути FRP и основной упор делаем на работу с данными. Другими словами: мы выстраиваем поток данных таким образом, чтобы приложение выполняло свои задачи. При этом, специфика basis.js такова, что клиентский код получается довольно линейным из-за отсутствия циклов и большого количества ветвлений. По крайней мере обозначенные задачи можно решить без них.

В следующих статьях мы будем улучшать наш клиент и наращивать его функционал.

Спасибо за интерес к basis.js!



Огромная благодарность lahmatiy за бесценные советы ;)



Несколько полезных ссылок:



Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/305292/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<вКонтакте - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda