Как и для чего мы сделали «Перчатку» — приложение для сотрудников «Перекрёстка» с элементами соцсети

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

В рознице «Перекрёстка» работает порядка 30 тыс. сотрудников без закрепленного рабочего места и персонального компьютера. Чтобы они могли активнее участвовать в жизни компании и коммуницировать с коллегами, мы разработали «Перчатку». Это приложение с чатом и корпоративными сервисами: графиком смен и отпусков, информацией о выплатах и другими возможностями вроде ведения блогов и комментирования публикаций коллег.

Инструментарий «Перчатки» также включает чат-бота «Василису», которая помогает новичкам влиться в коллектив: сопровождает в первые дни, находит корпоративные онлайн-курсы. Отличительной особенностью проекта являются элементы геймификации — за активность в «Перчатке» пользователи получают специальные баллы («клеверы»), на которые можно приобретать «сувенирку».

Помимо младших сотрудников торговой сети, «Перчаткой» пользуются технические специалисты и сами разработчики. Им приложение помогает собирать фидбек о функциях, а также краудсорсить новые идеи.

Как мы подошли к проекту

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

Если говорить о кастомизации, то на рынке, конечно же, есть неплохие готовые платформы вроде rocket.chat. Но, с нашей точки зрения, они бы потребовали времени на изучение существующей кодовой базы, и — с учетом планов по интеграции корпоративных сервисов в качестве дополнения к чату — «на берегу» нам было сложно рассчитать, сколько ресурсов уйдет на доработку.

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

Всю коммуникацию «Перчатки» мы построили на связке REST + WebSocket, а в основу разработки положили React Native, поскольку он позволяет проектировать интерфейсы и анимации под кроссплатформенные приложения. В сети можно встретить мнение, что React — медленный инструмент, который плохо подходит для чего-либо кроме SPA, и действительно, можно найти сайты на нем, которые долго грузятся и медленно исполняются. Но в то же время есть огромное количество контрпримеров вроде Instagram. Существует специальный свод правил [тут и тут], следуя которым не составит труда сделать приложения на React Native производительными. Есть и множество других способов оптимизации — например, анимации исключительно в UI-треде с помощью Reanimated.

Особенности реализации

React Native поддерживает библиотеки, автоматизирующие отдельные компоненты пайплайна разработки. В частности, react-native-image-crop-picker упрощает работу с мультимедиа, а react-native-sqlite-storage помог нам с кешированием сообщений в базе данных. Сейчас мы храним сообщения на стороне клиента — так проще загружать историю переписки пользователя.

Обычно для решения этой задачи используют AsyncStorage, который парсит сообщения при запуске приложения. Нам такой вариант не подходил, поскольку он замедляет работу системы, а большинство пользователей из целевой аудитории и так владеет не самыми мощными устройствами. Чтобы снизить нагрузку на «железо», мы использовали компактную SQLite и подготовили спец. запросы — например, вот так выглядит запрос на создание базы:

const db = await SQLite.openDatabase({ name: DATABASE_NAME, location: "default" })
await db.executeSql(`CREATE TABLE IF NOT EXISTS messages (
      id VARCHAT(32) PRIMARY KEY NOT NULL,
      user TEXT,
      keyboard TEXT,
      canEdit BOOLEAN,
      deleted BOOLEAN,
      forwardedMessage TEXT,
      type VARCHAT(32),
      mentions TEXT,
      status TINYINT,
      text TEXT,
      emotions TEXT,
      time BIGINT,
      attachments TEXT,
      chatId VARCHAT(32)
    )`)

А вот так — запрос на получение сообщений:

const db = await this.getInstance()
const dbMessages = await db.executeSql(
	"SELECT * FROM messages m WHERE m.chatId = ? AND m.time <= ? ORDER BY time DESC LIMIT 16",
[chatId, date],
)

Если говорить о других технологиях, которые мы задействовали на разных этапах разработки, это Nest.js и MongoDB и Socket.io. На них построен наш чат-бот «Василиса». Из интересных библиотек можно выделить Reanimated и Redux Saga. Первая помогает анимировать UI-элементы, вторая — отвечает за бизнес-логику.

Что пошло не так

Сложности с обработкой скроллинга. За отображение сообщений отвечает компонент FlatList. Он заточен под работу с длинными списками и рендерит только видимые на экране элементы, однако не позволяет задавать начальную позицию скролла, если высота списка заранее неизвестна. Чтобы обойти это ограничение, мы сперва отображаем старые сообщения и только потом сохраняем позицию скролла. Но если на iOS это можно сделать с помощью компонента maintainVisibleContentPosition, то на Android такой функциональности нет.

Изначально мы решили проблему добавлением «невидимого списка», куда по умолчанию попадали все новые сообщения. Мы замеряли его высоту после рендера, использовали это значение для прокрутки основного списка и просто меняли их местами. Такой подход эмулировал загрузку без скролла, но работал медленно. В итоге мы обратились к нативному модулю React, который использует UIManagerModuleListener и запоминает позицию скролла при вызове willDispatchViewUpdates. Теперь на каждую команду onLayoutUpdated список прокручивается до запомненного значения. К слову, на аналогичном подходе построены библиотеки для работы со списками вроде flat-list-mvcp.

Проблемы с реплаями. Эту функциональность реализует метод scrollToIndex, но он не работает, если сообщение, на которое нужно ответить, еще не было отображено (как мы уже говорили, FlatList не рендерит все элементы сразу). Проблему решили просто — если scrollToIndex завершился с ошибкой, вызываем его еще раз, так как к этому моменту система отображает больше сообщений.

handleOnScrollToIndexFailed(info: {index: number; averageItemLength: number; highestMeasuredFrameIndex: number }) {
		const { index } = info
		this.scroll.current?.scrollToOffset({ offset: info.averageItemLength * info.index, animated: true })
		setTimeout(() => {
			this.scroll.current?.scrollToIndex({ index, animated: true })
		}, 100)
	}

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

Перегрузка бэкенда. С этой проблемой мы столкнулись уже в процессе эксплуатации «Перчатки». Сотрудники начали использовать приложение для проведения марафонов. Так мы называем чаты, в которых коллеги получают задания и отписываются об их выполнении. В какой-то момент число участников в них стало исчисляться сотнями, и бэкенд перестал обрабатывать запросы.

Оказалось, что при посещении главного экрана система подтягивала список пользователей для всех чатов. Фикс достаточно прост — мы начали подгружать информацию только при открытии конкретной «разговорной комнаты». Теперь кратковременно видны системные имена пользователей на латинице, прежде чем они отображаются на кириллице, но это не мешает работе в приложении.

Что дальше?

За две недели после запуска в «Перчатке» зарегистрировалась четверть сотрудников «Перекрестка», каждый день в приложение заходит 20% из них, и эти показатели растут. Мы продолжим наблюдать за работой проекта в боевом режиме и будем исправлять возникающие баги, оттачивать функциональность и улучшать внутренние процессы. Уже сейчас мы прорабатываем инструментарий для взаимодействия с аудиторией на конференциях, тестируем опросы для оценки настроений персонала и обдумываем дополнительные возможности.

Источник: https://habr.com/ru/company/X5Group/blog/577616/


Интересные статьи

Интересные статьи

Значительная часть жизни уже давно перетекла в гаджеты, онлайн-сервисы, соцсети и мессенджеры, которые ежедневно собирают тонны персональных данных. А ими часто обмениваются компании, например, в сфер...
Поддержка влияет на отток клиентов, продажи по сарафанке. В данной статье мы расскажем о нашем опыте и поделимся рецептами, которые помогли стать поддержке нашего дата-це...
Привет! В этой статье я хочу рассказать, как из модного микросервисного приложения можно сделать рабочую, управляемую систему с помощью трех проверенных годами методик: на примере про...
Эта статья о том, как устроена графика в Linux и из каких компонентов она состоит. В ней много скриншотов с различными реализациями сред рабочих столов.  Если вы не сильно различае...
Александр Епанешников, 19-летний российский студент, формально является слепым *. Недавно он решил, что хочет быть более независимым и меньше полагаться на маму при поездках в школу. Путь до школ...