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

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

В этой статье мы разработаем приложение для ежедневных отчетов с использованием NCMB (мобильная серверная часть NIFCLOUD). Эта статья разделена на две части. Первая половина представляет собой обзор приложения и объяснение процесса аутентификации.

Используемые технологии и библиотеки

В этом приложении используются следующие технологии и библиотеки:

  • Фреймворк7 v8
  • Мобильный сервер NIFCLOUD
  • dayjs (библиотека javascript для обработки дат)

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

Экраны и разрешения

Начальный экран

Это экран, на котором пользователь вводит ежедневный отчет. Это первый экран, который появляется после открытия приложения. Поля ввода на этом экране следующие:

  • Дата: Дата отчета
  • Задание: Описание выполненной работы
  • Категория: Категория работы
  • Время: Продолжительность работы

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

Экран аутентификации

Экран аутентификации объединяет вход пользователя и регистрацию нового пользователя в один экран. Здесь вводятся следующие поля:

  • Отображаемое имя
  • ID пользователя
  • Пароль

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

Экран списка ежедневных отчетов

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

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

Разрешения для данных отчета

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

Разрешения на использование пользовательских данных

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

Установка библиотек

Получить мобильный серверный ключ NIFCLOUD

Зарегистрировавшись или войдя в Мобильный сервер NIFCLOUD, вы можете создать приложение и получить следующие два ключа:

  • Ключ приложения
  • Ключ клиента

Установка библиотеки

Теперь мы можем приступить к разработке приложения. Для этого проекта мы используем шаблон Framework7. Мы выбрали простой шаблон JavaScript без использования каких-либо внешних фреймворков (таких как Vue или React).

В качестве внешних библиотек добавим ncmb и dayjs. Обе они являются библиотеками JavaScript, поэтому для их загрузки используйте «Добавить и удалить компоненты JS/CSS» в разделе «Настройка» в Monaca Cloud. Мы также можем использовать dayjs из CDN.

Не забудьте загрузить файл `ncmb.min.js` как скрипт в файле index.html.

 <!-- Ncmb script -->
 <script src="components/ncmb/ncmb.min.js"></script>
 <!-- Dayjs script -->
 <script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
 <script>dayjs().format()</script>

Время кодирования

Инициализация NCMB

Создайте файл в разделе www/js/ с именем app.js и инициализируйте мобильную серверную часть NIFCLOUD следующим образом. Включите ключ приложения и ключ клиента, которые вы получили в процессе «Получить мобильный бэкэнд-ключ NIFCLOUD».

var ncmb;
document.addEventListener('DOMContentLoaded', e => {
    const applicationKey = '...';
    const clientKey = '...';
    ncmb = new NCMB(applicationKey, clientKey);
}

Введение в index.html

В файле www/index.html, который является исходным файлом, загружаемым в приложение Monaca, мы создаем панель вкладок с помощью Framework7. Загружаются следующие файлы: entry.html, экран для ввода ежедневных отчетов, иbrowse.html, экран для их просмотра.

  <!-- Views/Tabs container -->
  <div class="views tabs safe-areas">
      <!-- Tabbar for switching views-tabs -->
      <div class="toolbar toolbar-bottom tabbar-labels">
          <div class="toolbar-inner">
              <a href="#view-entry" class="tab-link tab-link-active">
                  <i class="icon f7-icons if-not-md">house_fill</i>
                  <i class="icon material-icons if-md">home</i>
                  <span class="tabbar-label">New Entry</span>
              </a>
              <a href="#view-browse" class="tab-link" id="browseTab">
                  <i class="icon f7-icons if-not-md">square_list_fill</i>
                  <i class="icon material-icons if-md">view_list</i>
                  <span class="tabbar-label">Browse</span>
              </a>
          </div>
      </div>

      <!-- Your main view/tab, should have "view-main" class. It also has "tab-active" class -->
      <!-- Entry View -->
      <div id="view-entry" class="view view-main view-init tab tab-active" data-name="new_entry" data-url="/new_entry/">
          <!-- New entry page will be loaded here dynamically from /catalog/ route -->
      </div>

      <!-- Browse View -->
      <div id="view-browse" class="view view-init tab" data-name="browse" data-url="/browse/">
          <!-- Browse page will be loaded here dynamically from /settings/ route -->
      </div>
  </div>

Реализация экрана ввода

Вот пример HTML-кода экрана ввода `entry.html`, который в первую очередь отображает поля ввода для ежедневного отчета и содержит элемент `#reports` для отображения введенного содержимого.

<div class="page" data-name="new_entry">
    <div class="navbar">
        <div class="navbar-bg"></div>
        <div class="navbar-inner sliding">
            <div class="title">Daily Report Entry</div>
        </div>
    </div>
    <div class="page-content">
        <div class="block">
            <div class="list list-strong-ios list-dividers-ios inset-ios">
                <ul>
                    <li class="item-content item-input">
                        <div class="item-inner">
                            <div class="item-title item-label">Date</div>
                            <div class="item-input-wrap">
                                <input id="date" type="date" value="2023-06-01" placeholder="Please choose...">
                            </div>
                        </div>
                    </li>
                    <li class="item-content item-input">
                        <div class="item-inner">
                            <div class="item-title item-label">Task</div>
                            <div class="item-input-wrap">
                                <input id="description" type="text" placeholder="Description of work">
                                <span class="input-clear-button"></span>
                            </div>
                        </div>
                    </li>
                    <li class="item-content item-input">
                        <div class="item-inner">
                            <div class="item-title item-label">Time</div>
                            <div class="item-input-wrap">
                                <div class="range-slider range-slider-init" data-label="true">
                                    <input id="time" type="range" value="8" min="0" max="10" step="1">
                                </div>
                            </div>
                        </div>
                    </li>
                    <li>
                        <div class="item-media">
                            <i class="icon demo-list-icon"></i>
                        </div>
                        <a class="item-link smart-select smart-select-init" data-open-in="popover">
                            <select id="category" name="category">
                                <option value="option" selected>Option</option>
                            </select>
                            <div class="item-content">
                                <div class="item-inner">
                                    <div class="item-title">Category of Work</div>
                                </div>
                            </div>
                        </a>
                    </li>
                </ul>
            </div>
            <button id="addItem" class="button button-large button-raised button-fill margin-top" style="width: 50%; margin: auto;">Send Report</button>
        </div>
        <hr class="margin-horizontal" />
        <div class="block">
            <div class="block-title text-align-center" style="font-size: 20px; color: black; height: 20px;">Registered Items</div>
            <div class="list list-strong list-outline list-dividers-ios simple-list">
                <ul id="reports"></ul>
            </div>
        </div>
    </div>
</div>

О JavaScript

В JavaScript мы проверяем, вошел ли пользователь в систему при первом отображении экрана. Эта логика реализована в функции document.addEventListener(‘DOMContentLoaded’, e =› {}, которая вызывается при отображении экрана ввода.

document.addEventListener('DOMContentLoaded', e => {
//Check if the user is logged in
    checkAuth();
}
var loginPopup;
//Redirect the user to the appropriate screen according to their login status
async function checkAuth() {
    const loggedIn = await loginCheck.bind(this)();
    if (loggedIn) {
        console.log("logged in")
    } else {
        //If the user is not logged in, open the login-signup popup
        console.log('not logged in')
        loginPopup = app.popup.create({
            el: "#loginScreen"
        });
        loginPopup.open();
    }
}

Чтобы проверить статус входа в систему, мы будем использовать функцию управления пользователями мобильного бэкэнда NIFCLOUD. Во-первых, мы используем ncmb.User.getCurrentUser() для получения информации о текущем вошедшем в систему пользователе. Если это возвращает пустое значение, это означает, что пользователь не вошел в систему, и мы откроем всплывающее окно входа в систему.

//Check the login status of the user
async function loginCheck() {
    const user = ncmb.User.getCurrentUser();
    if (!user) {
        // User is not logged in
        return false;
    }
    try {
        // Validate the session
        await ncmb.DataStore('Test').fetch();
        return true;
    } catch (e) {
        // If the session is invalid, an error will occur. 
        ncmb.sessionToken = null;
        return false;
    }
}

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

Реализация всплывающего окна входа

Вот реализация всплывающего окна login.html, которое отображается, когда пользователь не вошел в систему. HTML-код является частью файла index.html и выглядит следующим образом. Пользователи могут ввести свой идентификатор пользователя, пароль и отображаемое имя.

<!-- Login Popup -->
<div class="popup login-popup" id="loginScreen">
    <div class="page login-screen-page">
        <div class="page-content login-screen-content">
            <div class="login-screen-title">Login / Sign Up</div>
            <form>
                <div class="list">
                    <ul>
                        <li class="item-content item-input">
                            <div class="item-inner">
                                <div class="item-title item-label">Display Name</div>
                                <div class="item-input-wrap">
                                    <input type="text" name="display_name" id="displayName" placeholder="Your display name" />
                                    <span class="input-clear-button"></span>
                                </div>
                            </div>
                        </li>
                        <li class="item-content item-input">
                            <div class="item-inner">
                                <div class="item-title item-label">Username</div>
                                <div class="item-input-wrap">
                                    <input type="text" name="username" id="username" placeholder="Your username" />
                                    <span class="input-clear-button"></span>
                                </div>
                            </div>
                        </li>
                        <li class="item-content item-input">
                            <div class="item-inner">
                                <div class="item-title item-label">Password</div>
                                <div class="item-input-wrap">
                                    <input type="password" name="password" id="password" placeholder="Your password" />
                                    <span class="input-clear-button"></span>
                                </div>
                            </div>
                        </li>
                    </ul>
                </div>
                <div class="list inset">
                    <ul>
                        <li><a class="list-button" id="loginBtn">Log In / Sign Up</a></li>
                    </ul>
                </div>
            </form>
        </div>
    </div>
</div>

Процесс аутентификации запускается при нажатии кнопки входа (`#login`). Этот процесс должен быть реализован, когда все содержимое DOM уже загружено.

document.addEventListener('DOMContentLoaded', e => {
    //Add login-signup button click event listener
    document.querySelector("#loginBtn").onclick = () => login.bind(this)();
}

Давайте объясним фактическую функцию входа в систему. Сначала мы выполняем процесс регистрации пользователя `registerUser`. Если пользователь уже зарегистрирован, этот процесс приведет к ошибке дублирования идентификатора пользователя. Поэтому мы используем блок try/catch для продолжения выполнения даже в случае возникновения ошибки.

//Perform authentication process
async function login() {
    const userName = document.querySelector('#username').value;
    const password = document.querySelector('#password').value;
    const displayName = document.querySelector('#displayName').value;
    //If these credentials have already been registered, the function will throw an error
    try {
        var user = new ncmb.User();
        user.set("userName", userName)
            .set("password", password)
            .set("displayName", displayName)
        await user.signUpByAccount();
    } catch (e) {
        console.log(e);
    }
    try {
        //Login process
        await ncmb.User.login(userName, password);
        //If the login is a success we close the popup
        loginPopup.close();
    } catch (e) {
        app.dialog.alert('Login failed. Please check your username and password.')
        return false;
    }
}

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

Если вход выполнен успешно, мы закрываем всплывающее окно входа.

Экран управления мобильным сервером NIFCLOUD

Наконец, на экране управления мобильного бэкэнда NIFCLOUD мы создадим группу управления. В разделе управления пользователями на экране управления мы создадим роль. Для ясности назовем его «admin».

Кроме того, мы добавим поле «admin» для этого пользователя и установим для него значение true.

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

Сводка

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

Вы можете найти код этого проекта в этом репозитории GitHub.