Synchra.24
menu
menu_bookДокументация

Практические материалы по запуску и развитию

Здесь собраны инструкции по запуску, разработке внутренних сценариев и встраиванию собственных инструментов в рабочий контур компании.

folderВстроенные сервисы

Bridge library

Оригинальная документация пакета @synchra24/app.

TypeScript SDK для гибридных приложений, которые открываются внутри мобильного приложения Synchra.24 через WebView.

Пакет оборачивает native bridge Synchra и предоставляет Promise-based API для вызова возможностей приложения: запросы к разрешенным API, контекст провайдера, тема, геопозиция, выбор файлов и медиа, получение файлов из S3, QR-сканер, выбор пользователей, календарь, выбор времени, snackbar и модальное окно подтверждения.

Установка

npm install @synchra24/app
import { SynchraApp } from "@synchra24/app";

const synchra = new SynchraApp("SERVICE_TOKEN");

SERVICE_TOKEN - токен внутреннего сервиса, созданный в Synchra.24. Native-приложение проверяет этот токен при каждом bridge-вызове.

Быстрый старт

const synchra = new SynchraApp("SERVICE_TOKEN");

const integrated = await synchra.isSynchraIntegrated();

if (!integrated) {
  console.log("Приложение открыто не внутри Synchra.24 или токен невалиден.");
}

const provider = await synchra.getProviderContext();
const theme = synchra.getSynchraTheme();

await synchra.showSnackbar({
  message: `Provider ID: ${provider.provider_id}`,
  type: "success",
});

console.log(theme?.active);
console.log(theme?.current.controlPrimary);

Создание клиента

const synchra = new SynchraApp(token, {
  responseTimeout: 60000,
});

Параметры:

  • responseTimeout - таймаут ожидания ответа от native bridge в миллисекундах. По умолчанию 60000.

Когда экземпляр больше не нужен, можно снять listeners и отклонить ожидающие запросы:

synchra.destroy();

Ошибки

Большинство методов возвращают Promise. Если native-приложение вернуло ошибку, Promise будет отклонен через Error(message).

try {
  const position = await synchra.getGeoPosition();
} catch (error) {
  console.error(error);
}

Метод isSynchraIntegrated() специально сделан безопасным: он возвращает false, если bridge недоступен или токен не прошел проверку.

Проверка интеграции

Проверяет, что приложение открыто внутри Synchra.24 и service token валиден.

const integrated = await synchra.isSynchraIntegrated();

Возвращает:

boolean;

Тема приложения

Synchra прокидывает цветовую тему напрямую в WebView, поэтому чтение темы не требует bridge-запроса.

const theme = synchra.getSynchraTheme();

Возвращает:

{
    active: 'light' | 'dark';
    current: Record<string, string>;
    themes: {
        light: Record<string, string>;
        dark: Record<string, string>;
    };
} | null

Подписка на изменение темы:

const unsubscribe = synchra.onSynchraThemeChange((theme) => {
  document.body.style.background = theme.current.primary;
});

unsubscribe();

Также доступен legacy bridge активной темы:

const activeTheme = await synchra.getThemeState();

const unsubscribeAppearance = synchra.onThemeAppearance((theme) => {
  console.log(theme);
});

API-запросы

Выполняет разрешенные Open API запросы через native-приложение Synchra.

const response = await synchra.requestApi<{ parameters: { name: string } }>({
  method: "GET",
  path: "providers/parameters",
  query: {
    key: "value",
  },
});

console.log(response.result.parameters.name);

Payload:

{
    method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
    path: string;
    body?: Record<string, unknown>;
    query?: Record<string, unknown>;
    apiVersion?: number;
    form?: Record<string, unknown> | { name: string; value: unknown }[];
}

apiVersion позволяет вызывать не только /api/v1, но и другие версии API. Если передать apiVersion: 2, запрос уйдет на /api/v2/....

form нужен для multipart/form-data запросов. Поскольку bridge сериализуется через postMessage, в form нужно передавать обычный JSON-совместимый объект или массив полей, а native-слой сам соберет FormData.

Пример multipart-запроса:

await synchra.requestApi({
  method: "POST",
  path: "reports/create",
  apiVersion: 1,
  form: {
    title: "Отчет",
    published: true,
    tags: ["daily", "shift"],
  },
});

Контекст провайдера

Возвращает текущий контекст провайдера Synchra без provider token.

const provider = await synchra.getProviderContext();

console.log(provider.provider_id);
console.log(provider.profile_id);
console.log(provider.role.permissions);
console.log(provider.bucketName);

Provider token этим методом не возвращается.

Выбор пользователей

Открывает native-виджет выбора пользователей.

const result = await synchra.selectUsers({
  users: [25],
  all_users: true,
});

console.log(result.users);

Payload:

{
    users?: number[];
    all_users?: boolean;
}

Возвращает:

{
    users: number[];
}

Геопозиция

Запрашивает текущую геопозицию пользователя через native-приложение.

const position = await synchra.getGeoPosition();

console.log(position.latitude, position.longitude);

Возвращает:

{
  latitude: number;
  longitude: number;
  altitude: number | null;
  accuracy: number | null;
  altitude_accuracy: number | null;
  heading: number | null;
  speed: number | null;
  timestamp: number;
}

Фото с камеры

Открывает native-камеру и возвращает фото. По умолчанию файл загружается в хранилище Synchra, а в ответе приходят file, bucket и base64.

const photo = await synchra.takePhoto({
  use_front_camera: false,
  upload: true,
});

console.log(photo.file);
console.log(photo.bucket);
console.log(photo.base64);

Если нужен только base64, загрузку можно отключить:

const photo = await synchra.takePhoto({
  upload: false,
});

console.log(photo.file); // null
console.log(photo.bucket); // null
console.log(photo.base64);

Возвращает:

{
  file: string | null;
  bucket: string | null;
  base64: string;
  name: string;
  type: string;
}

Фото из галереи

Открывает native-выбор фото из галереи. Формат ответа такой же, как у takePhoto().

const photo = await synchra.pickPhoto({
  upload: false,
});

Выбор файла

Открывает native file picker. Файл всегда копируется в локальный cache приложения и возвращается как local_uri. При необходимости он дополнительно загружается в хранилище Synchra.

const file = await synchra.pickFile({
  upload: true,
});

console.log(file.file);
console.log(file.bucket);
console.log(file.local_uri);
console.log(file.name);
console.log(file.size);

Если нужен только локальный файл без загрузки в Synchra, загрузку можно отключить:

const file = await synchra.pickFile({
  upload: false,
});

Возвращает:

{
  file: string | null;
  bucket: string | null;
  local_uri: string;
  name: string;
  type: string;
  size: number;
}

QR-сканер

Открывает native QR-сканер.

const qr = await synchra.scanQr();

console.log(qr.value);
console.log(qr.type);

Возвращает:

{
    value: string;
    type?: string;
}

Получение изображения из S3

Получает файл из S3 через native-приложение. По умолчанию возвращает локальный путь local_uri, потому что это заметно легче для bridge и лучше подходит для больших изображений.

const image = await synchra.getS3Image({
  file: "avatar.jpg",
});

console.log(image.local_uri);

Если нужен именно base64, можно явно запросить его:

const image = await synchra.getS3Image({
  file: "avatar.jpg",
  response_type: "base64",
});

Возвращает:

{
  file: string;
  local_uri: string | null;
  base64: string | null;
  response_type: "local_uri" | "base64";
  source_url: string;
}

Получение файла из S3

Работает так же, как получение изображения, но предназначен для любых файлов.

const file = await synchra.getS3File({
  file: "docs/report.pdf",
});

console.log(file.local_uri);

Для обоих S3-методов bucket снаружи не передается. Native-слой всегда берет его из providerCardContext.bucketName текущего провайдера.

Навигация по внутренним экранам

Позволяет из гибридного приложения открывать внутренние экраны Synchra.

await synchra.navigate({
  action: "navigate",
  screen: "Tasks",
});

С параметрами:

await synchra.navigate({
  action: "navigate",
  screen: "TaskCard",
  params: {
    number: "123",
  },
});

Поддерживаемые действия:

  • navigate
  • push
  • replace
  • go_back
  • pop_to_top

Пример возврата назад:

await synchra.navigate({
  action: "go_back",
});

Возвращает:

{
  success: boolean;
  action: "navigate" | "push" | "replace" | "go_back" | "pop_to_top";
  screen: string | null;
}

Если указать неизвестный экран, native bridge вернет ошибку screen not found.

Выбор даты

Открывает native date picker для выбора одной даты.

const result = await synchra.selectDate({
  date: "2026-04-21",
});

console.log(result.date);

Возвращает:

{
  date: string | null; // YYYY-MM-DD
}

Выбор диапазона дат

Открывает native date range picker.

const range = await synchra.selectDateRange({
  start_date: "2026-04-01",
  end_date: "2026-04-21",
});

console.log(range.start_date, range.end_date);

Возвращает:

{
  start_date: string | null;
  end_date: string | null;
}

Выбор времени

Открывает native time picker.

const time = await synchra.selectTime({
  time: "09:30",
});

console.log(time.time);
console.log(time.hours);
console.log(time.minutes);

В payload можно передать либо time, либо hours/minutes.

Возвращает:

{
  time: string; // HH:mm
  hours: number;
  minutes: number;
}

Snackbar

Показывает native snackbar Synchra.

await synchra.showSnackbar({
  message: "Сохранено",
  type: "success",
  timeout: 4000,
});

Доступные типы:

  • success
  • error
  • warn
  • empty

Возвращает:

{
  shown: boolean;
}

Модальное окно

Открывает native-модальное окно Synchra с двумя кнопками.

const result = await synchra.openModal({
  title: "Подтверждение",
  text: "Продолжить действие?",
  primary_button_text: "Продолжить",
  secondary_button_text: "Отмена",
});

if (result.action === "primary") {
  // Нажата первая кнопка
}

if (result.action === "secondary") {
  // Нажата вторая кнопка
}

if (result.action === "dismiss") {
  // Окно закрыто нажатием по фону
}

Возвращает:

{
  action: "primary" | "secondary" | "dismiss";
}

TypeScript

Пакет поставляется с декларациями TypeScript. Можно импортировать типы payload и response:

import type {
  SynchraProviderContext,
  SynchraPickedFilePayload,
  SynchraThemePayload,
} from "@synchra24/app";

Запуск вне Synchra

Большинство bridge-методов требуют window.ReactNativeWebView. При вызове вне мобильного приложения Synchra они выбросят ошибку. Для безопасной runtime-проверки используйте isSynchraIntegrated().

const integrated = await synchra.isSynchraIntegrated();

if (!integrated) {
  // Показать fallback UI для обычного браузера.
}

Разработка

Установить зависимости:

npm install

Проверить типы:

npm run typecheck

Собрать пакет:

npm run build

Проверить состав публикации:

npm pack --dry-run

В npm-пакет попадают только dist, README.md и package.json.

Публикация

Перед публикацией нужно убедиться, что native-приложение Synchra поддерживает те же bridge-события, что и SDK.

npm publish

Для публичной публикации scoped-пакета:

npm publish --access public