# Nanc — backend-agnostic CMS с Flutterлюшками # Intro Привет! Сегодня я хочу представить вам плод моего многомесячного труда по ночам и выходным, призванный улучшить опыт управления контентом и привнести в мир разработки Flutter-приложений дополнительные возможности. Далее речь пойдет о Nanc (читается как Нэнс, но я внутренним голосом постоянно произношу "Нанк" 🤪) - **N**ot **A** **N**ormal **C**MS. Почему она "не нормальная" и что с её помощью можно делать вы узнаете, осилив эту статью. Я не знал, стоит ли добавлять статью в "Я пиарюсь", так как это не реклама чего-либо (рекламировать пока нечего - вы не сможете использовать Nanc прямо сейчас для своих проектов), но, все же, добавил, чтобы никого особо чувствительного не задеть 😉 *(надеюсь, я не задел никого особо чувствительного предыдущим предложением, или даже этим)*. Но кое с чем вы, все таки, поиграть сможете. Для того чтобы показать общественности возможности Nanc были сделаны два демо-приложения: клиентское, имитирующее любое Flutter-приложение, и дающее представление о том, что может дать приложениям на Flutter Nanc. И CMS-приложение Nanc, представляющее из себя предварительно сконфигурированную CMS с дополнительно внедренным слоем логики синхронизации клиентского приложения с CMS. > В конце большинства логических блоков текста вы обнаружите youtube-видео с примером использования / демонстрацией того или иного аспекта Nanc. # Table of contents 1. Intro 2. About CMS 1. Types of models 1. Collection 2. Solo 2. Editor 1. Общее описание 2. Code-first 3. Interface-first 4. Hybrid mode 3. Fields 1. Bool 2. Color 3. Date 4. Dynamic 5. Enum 6. Header 7. Icon 8. Id 9. MultiSelector 10. Number 11. Selector 12. String 13. Structure 14. Screen 3. Dynamic Flutter apps 1. Interactive documentation 2. Extensibility 3. Simplicity 4. The Power 5. Convenience 6. Performance 4. Nanc demo apps 1. Общее описание 2. Client 3. Admin 4. Connection manager 5. What's next / Afterwords # About CMS Итак. Nanc - это backend-agnostic CMS, которая не тянет за собой свой собственный бэкенд. Как это работает? Nanc предлагает реализовать интерфейсы сетевых сервисов, в которых прямо сейчас 6 методов, но к моменту релиза будет около 10. Много это или мало? Например, для реализации API для демо-приложения понадобилось написать 170 строк кода. Эти методы отвечают за всю работу Nanc с вашим существующим бэкендом. Реализация должна быть написана на языке Dart (язык разработки и для Flutter). В будущем Nanc будет поставляться с уже готовыми вариантами реализации этих интерфейсов под определенные варианты бэкендов - Firebase, Supabase, локальный / сетевой SQLite и Generic-реализация REST и GraphQL (может что-то еще?) и вам не придется думать об этой реализации вообще или придется, но совсем чуть-чуть. ## Types of models Модель в Nanc - это структурное описание какой-либо сущности, которой вы хотите управлять с помощью Nanc. Модель содержит название сущности, различные визуальные параметры и описание полей. ### Collection Коллекция - это некая сущность, у которой может существовать множество вариаций. Список пользователей, книг, отзывов - хорошие примеры моделей типа "Коллекция" в Nanc. Если вы знакомы с реляционными базами данных, то примером Коллекции в Nanc будет практически любая таблица в вашей базе. <iframe width="800" height="600" src="https://www.youtube.com/embed/_kOmKhZ2xlc" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Solo Соло (модель) - это сущность, существующая в единственном экземпляре. Например - Список Feature Toggles, или конфигурация чего-либо или..."Главный экран мобильного приложения". В целом Solo-модели призваны повысить удобство работы, являясь всего-лишь проекцией вашей базы данных. И Solo-моделью легко может быть и какая-нибудь таблица вашей БД, с одной-единственной записью. Но на текущий момент реализация этого класса моделей обязывает, чтобы запись этой модели *(строка в БД)* имела `id`, аналогичный `id` самой модели. ```dart final Model landingPage = Model( name: 'Landing page', /// ? id in format [toSnakeCase(name)] will be set automatically, if omitted // id: 'landing_page', isCollection: false, icon: IconPackNames.flu_document_page_break_regular, fields: [ ... ``` <iframe width="800" height="600" src="https://www.youtube.com/embed/JI094MMyjGI" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ## Editor ### Общее описание Nanc может быть сконфигурирован несколькими путями: кодом, с помощью интерфейса самого Nanc и комбинацией из этих вариантов. ### Code-first config Говоря про "конфигурацию", в первую очередь, подразумевается описание структуры ваших моделей. Возьмем один простой пример, модель Feature - сущности, описывающей особенности какого-либо продукта. Эта сущность содержит следующие поля: - id - title - image - description А реализация в виде code-first config будет иметь следующий вид: ```dart import 'package:fields/fields.dart'; import 'package:icons/icons.dart'; import 'package:model/model.dart'; final Model feature = Model( name: 'Feature', icon: IconPackNames.flu_ribbon_star_filled, fields: [ [ IdField(width: 200), StringField(name: 'Title', maxLines: 1, isRequired: true, width: 400), ], [ IconField(name: 'Image', isRequired: true), ], [ StringField(name: 'Description', isRequired: true, showInList: true), ], ], ); ``` Описав такую модель и внедрив ее в Nanc CMS вам становится доступным весь CRUD этой модели. ### Interface-first config Точно такую же модель Feature (назовем ее Feature Variant) мы могли бы создать и через интерфейс Nanc. При этом (учитывая, что все подготовительные работы для использования Nanc сделаны) - при создании модели в Nanc вы сразу же создадите и таблицу в базе данных, и точно также весь CRUD вам будет сразу доступен. Также, можно пойти по более безопасному пути, не создавая в базе данных ничего при создании модели через интерфейс Nanc. А самостоятельно создать таблицу в вашей БД, а затем, под нее создать модель в Nanc. К слову, именно так вам придется делать, если вы описываете конфигурацию кодом - из нее в вашей БД новые таблицы не появятся. <iframe width="800" height="600" src="https://www.youtube.com/embed/nirJOrh0uvI" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Hybrid config Этот вариант становится вам доступен, когда вы, имея Code-first config, решили изменить его посредством интерфейса Nanc. В этом случае все дальнейшие изменения этой модели будут возможны только через интерфейс, а изменения, внесенные и оригинальную code-модель будут проигнорированы. Единственный способ вернуться к Code-first - это "сбросить" модель - в этом случае все изменения в структуре модели, внесенные через интерфейс, будут помножены на ноль _(жаль, что только они)_ и снова начнет использоваться актуальный Code-first config. Никакие данные при таком сбросе не затрагиваются, это касается только структуры модели. <iframe width="800" height="600" src="https://www.youtube.com/embed/Ti9dxAx4hYk" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ## Fields Теперь рассмотрим, какие типы полей Nanc поддерживает на данный момент. ### Boolean BoolField позволяет управлять типом данных `bool` и настраивать значение по умолчанию. <iframe width="800" height="600" src="https://www.youtube.com/embed/d7rbfgjk0o0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Color ColorField предоставляет вам удобный color-picker, позволяющий выбирать цвет сразу же в Nanc. А также дает возможность вносить изменения вручную, посредством редактирования AHEX-кода. AHEX - это классический HEX-Color (например - `#10A0CF`), но с дополнительным значением прозрачности, указываемым вначале. В данном случае этот цвет будет аналогичен цвету `#FF10A0CF` (`FF` - 100% непрозрачность - цвет полностью непрозрачен). А так бы выглядел этот же цвет с 50% непрозрачности: `#7F10A0CF`. <iframe width="800" height="600" src="https://www.youtube.com/embed/v6WHJeW4mwU" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Date DateField отвечает за управление датой и временем (оба значения сразу, отдельные для даты и времени будут реализованы позднее). DateField содержит два boolean-параметра, которые позволяют модифицировать поведение данного поля, сделав из него timestamp времени создания сущности и timestamp времени изменения. <iframe width="800" height="600" src="https://www.youtube.com/embed/4FdUpM9xIhU" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Dynamic DynamicField, с одной стороны - предельно простое поле, а с другой - включающее всю полноту сложности других полей. Изначально вы можете настроить у данного поля только внешний вид (то, как это поле будет выглядеть в интерфейсе Nanc - цвет и сопровождающая иконка). После этого, данное поле может содержать любые значения, доступные в Nanc, включая себя. Что это означает? По сути, DynamicField - это типизированный JSON с возможностью позиционирования полей по порядку внутри себя. <iframe width="800" height="600" src="https://www.youtube.com/embed/lhjw3ORLtkA" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Enum EnumField - поле для выбора какого-либо значения из нескольких значений. Правило, которого стоит придерживаться, выбирая EnumField - если у вас есть конечный список значений для выбора, не хранящийся в отдельной таблице базы данных - выбирайте Enum. В противном случае - SelectorField, подробнее о котором будет ниже. _TODO: В настоящий момент это поле может быть сконфигурировано только через CodeConfig, конфигурация через интерфейс не проработана._ <iframe width="800" height="600" src="https://www.youtube.com/embed/41IaJY45mvk" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Header HeaderField - не совсем поле, а визуальный улучшайзер вашей модели в Nanc. Вы можете использовать данное **не**поле для задания общего заголовка для группы связанных полей или для разграничения полей модели друг от друга, используя HeaderField как разделитель. <iframe width="800" height="600" src="https://www.youtube.com/embed/gjhwrlxRRkA" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Icon IconField предоставляет вам возможность выбирать иконку (класс `IconData`) из предопределенного набора иконок. На данный момент их около 25.000 и в этот набор входят следующие иконки: - [Material Design Icons](https://pub.dev/packages/material_design_icons_flutter) - [Fluent UI System Icons](https://pub.dev/packages/fluentui_system_icons) - [Remix Icons](https://pub.dev/packages/remixicon) При необходимости в базовый набор поставки могут быть добавлены и другие иконки, а в недалеком будущем будет предоставлена возможность использования и ваших собственных иконок. > Можно задаться вопросом "Иконки есть, цвета есть, а шрифты?". Если вы так и сделали, то ответ вы сможете найти в документации к виджету **Text**. Краткий ответ - вам доступны все шрифты из [Google Fonts](https://fonts.google.com/). <iframe width="800" height="600" src="https://www.youtube.com/embed/lGZXMOMAkw0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Id IdField - такое простое, но такое важное поле. В каждой модели, управляемой Nanc, должно присутствовать хотя бы одно поле типа Id. На данный момент поддерживается только строковый тип ID (он может быть любой уникальной, в рамках одной сущности, строкой). Планируется добавить поддержку и числового типа, что, впрочем, возможно реализовать и сейчас, просто типизируя данные поля числового типа в API-реализации. <iframe width="800" height="600" src="https://www.youtube.com/embed/6ZU2qZCEeFE" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Multi-selector MultiSelectorField - довольно непростое поле, отвечающее за реализацию реляционного отношения "многие ко многим" или "многие к одному". Есть три режима использования данного поля. Пройдемся более детально по каждому из них. _TODO: В настоящий момент это поле может быть сконфигурировано только через CodeConfig, конфигурация через интерфейс не проработана._ #### Array of ids Данный режим дает вам возможность хранить `id` связанных сущностей в родительсой сущности напрямую. Например - мы имеем две модели - Читатель и Книга. В данном режиме, книги, которые взял читатель к себе, будут учтены в виде хранящихся в поле модели Читателя идентификаторов. Например вот так: ```json5 /// user table { id: 'UUID', name: 'String', books: [ 'UUID', 'UUID' // ... ] } ``` > Выше представлена примерная структура таблицы, выраженная с помощью JSON5-синтаксиса. Минусом данного режима является ограниченная целостность данных. Если вы не реализуете автоматическое удаление устаревших (удаленных) идентификаторов книг из поля `books` Читателя - вы получите ошибки. <iframe width="800" height="600" src="https://www.youtube.com/embed/c-wJtIAotNs" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> #### Third table Классический режим обеспечения отношений из мира SQL. При использовании данного режима вы храните связи сущностей в отдельной таблице, и обеспечиваете 100% целостность данных. Следующая структура может являться примером данного режима: ```json5 [ /// user table { id: 'UUID', name: 'String' }, /// book table { id: 'UUID', title: 'String' }, /// user_books_relations table { user_id: 'UUID', book_id: 'UUID' } ] ``` > На 7й секунде видно небольшое подергивание и если присмотреться, то можно заметить, что изменился url страницы - это я так попытался скрыть баг: в режиме third table данные сохраняются в родительской странице только если она уже была сохранена 🤷🏼‍♂️ <iframe width="800" height="600" src="https://www.youtube.com/embed/S0pLvSezKIM" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> #### Array of objects В целом аналогичен Array of ids, только хранятся в родительской записи не идентификаторы, а целиком весь объект (в виде плоской структуры, без возможных связанных сущностей вложенной записи). Обладает тем же минусом, что и Array of ids, но несет и дополнительный - увеличенное использование хранилища. Однако область применения данного режима (по крайней мере на данный момент) - есть, и она очень важна. Но об этом мы поговорим несколько позднее. > В видео я немного забегаю вперед, показывая ScreenField, к этому мы еще вернемся <iframe width="800" height="600" src="https://www.youtube.com/embed/DuF7hNPCR8I" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> > В целом есть идея, сделать "не каноничные" режимы виртуальными - чтобы они так или иначе работали через Third table, а необходимые данные загружались при редактировании страницы (если это нужно). ### Number NumberField хранит числа - вот так просто и все. <iframe width="800" height="600" src="https://www.youtube.com/embed/64Vq1XzYSd0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Selector SelectorField схож с MultiSelectorField (как вы могли догадаться по их названиям), но немного проще - связь тут "один к одному" или "один ко многим", а режимов два. _TODO: В настоящий момент это поле может быть сконфигурировано только через CodeConfig, конфигурация через интерфейс не проработана._ #### Id Обычный для SQL вид обеспечения связи, когда в поле родительской записи хранится идентификатор связанной. Возьмем за основу примера Читателя. Кто это? В первую очередь это человек, а что есть у человека? Правильно! Город рождения (который наша Библиотека, почему-то, тоже захотела знать). ```json5 /// user table { id: 'UUID', name: 'String', birth_place_id: 'UUID' } ``` #### Object Очень схож с Array of objects из MultiSelectorField, но хранить мы будем одно единственное связанное значение в поле родительской записи. Минусы те же, плюсы, так же, будут описаны совсем чуть-чуть ниже. ### String StringField хранит строки. Это поле имеет одну персональную настройку, отвечающую за удобство редактирования в Nanc - параметр, ограничивающий максимальную высоту редактируемого поля. Если ваш текст будет большим - имеет смысл не указывать его вовсе, тогда поле будет подстраиваться под высоту текста. Если ограниченно большим - вы можете задать явную высоту поля (в строках) и тогда она всегда будет такой. И, наконец, для коротких строк вы можете задать значение в одну строку, и тогда данное поле не будет расширяться, сколько бы вы впоследствии в него не написали. <iframe width="800" height="600" src="https://www.youtube.com/embed/dz2r6zZU8nw" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Structure StructureField позволяет вам хранить в записях модели массив типизированных структур. Вы указываете тип хранимых данных и легко и просто можете ими управлять. Доступные типы для полей структуры - абсолютно все. Поэтому вы легко можете создать "Dynamic Structure Field Repeat". _TODO: В рамках демо добавлять в StructureField можно только "плоские" поля._ <iframe width="800" height="600" src="https://www.youtube.com/embed/PBipHesMM5E" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ### Screen ScreenField - поле, позволяющее вам написать целое приложение на Flutter, прямо в Nanc! Со ScreenField вы можете описать интерфейс отдельного...screen (экрана), обновлять его как захотите, и делать это в любой момент времени за несколько минут - без утомительных и нервозных ожиданий ревью от Apple и Google. Давайте немного подробнее разберем этот тип поля (а на самом деле - целое отдельное функциональное ответвление Nanc). <iframe width="800" height="600" src="https://www.youtube.com/embed/3RGVDr4usDA" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> # Dynamic Flutter apps С помощью ScreenField вы действительно можете создать интерфейс практически любой сложности прямо в браузере (и вашей IDE), а затем, не делая публикаций в сторах - обновить соответствующий экран в вашем приложении. Если вам необходимо проверять гипотизы быстро - это отличная возможность. Эта функциональность отлично подойдет для относительно простых (с точки зрения логики) страниц вашего приложения, которые, однако, должны меняться довольно часто. В будущем этот функционал будет расширен до состояния, когда вы сможете создать действительно все, что захотите, без каких-либо ограничений. Теперь пройдемся во всем аспектам создания динамических экранов с помощью Nanc. ## Interactive documentation Во Flutter много виджетов. Очень много. Что такое виджет? Это кирпичик функциональности, из которых вы собираете ваше приложение. Он может быть как только визуальным, так и логическим - с определенным поведением внутри. Nanc предоставляет обширный перечень реализованных виджетов, которые вы можете использовать для создания UI. Но чем больше возможностей - тем сложнее о них узнать... Поэтому редактор экрана в Nanc дает вам доступ к интерактивной документации, где вы можете узнать, какие виджеты реализованы на данный момент, какие у них есть параметры и настраиваемые свойства, а также, прямо в документации, посмотреть, как они влияют на внешний вид создаваемого интерфейса. <iframe width="800" height="600" src="https://www.youtube.com/embed/t_MVTEnz3Fk" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ## Simplicity Nanc позволяет создавать интерфейс в реальном времени, но главное - он позволяет делать это очень просто и быстро (на интерфейс демо-приложения ушло немногим больше 2х часов). Но назревает вопрос - а как создавать сам UI? В Nanc нет какого-то экзотического синтаксиса для описания UI, ровно как и "слишком" простых решений, вроде длинного, для таких задач, JSON, которые заставят вас возненавидеть процесс создания интерфейсов в Nanc. Результатом поиска оптимального решения является простой и понятный синтаксис XML. Все стандартные виджеты Flutter имеют ровно те же названия, но в виде XML. Например, виджет `SizedBox` в Nanc будет `<sizedBox>...</sizedBox>`, и это правило применяется ко всем виджетам без исключения. Если у виджета есть какое-то сложное свойство, то оно будет иметь такое-же (или более простое) название в виде XML, с префиксом `prop`. Например - у виджета `Container` есть комплексное свойство `boxDecoration`, у которого есть свои внутренние свойства. Так вот, это свойство в Nanc будет иметь следующий вид: `<prop:decoration>...</prop:decoration>`. Это правило применяется ко всем сложным свойствам. И последний аспект - аргументы, являющиеся относительно простыми - это параметры XML-тегов. Посмотрим на примере того же `SizedBox`: ```xml <sizedBox width="50" height="50"> ... </sizedBox> ``` При этом, для некоторых виджетов реализованы и дополнительные аргументы, упрощающие написание кода, и у `SizedBox` это аргумент `size`, задающий одновременно `width` и `height`. Все, что написано тут - есть в интерактивной документации, поэтому, если вы что-то забыли или хотите узнать - обращайтесь к ней и найдете там ответы на все вопросы. ## Extensibility Реализовать поддержку нового виджета - дело от 10 минут до нескольких часов. Да данный момент имплементированы практически все основные виджеты, из которых можно создать сложный интерфейс с логикой. Со временем в Nanc будут реализованы все доступные во Flutter виджеты и вы сможете сделать действительно все. Но это не все. Вы можете легко и просто реализовать свои собственные виджеты и использовать их в Nanc с помощью одной-двух строчек XML-кода. Например - в стандартной библиотеке Flutter нет виджета, позволяющего отобразить легко отобразить Carousel Slider с картинками. Вам придется писать реализацию самостоятельно или использовать какое-либо open source решение, например [такое](https://pub.dev/packages/carousel_slider). И, реализовав то, что вам нужно - вы можете очень легко интегрировать ваш виджет в Nanc и использовать его. <iframe width="800" height="600" src="https://www.youtube.com/embed/HF7Is_3SbZI" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ## The Power Nanc предоставляет не просто возможности по преобразованию XML-кода в интерфейс на Flutter. Nanc дает возможности шаблонизации и написания логики. Условная отрисовка элементов, отрисовка в цикле, обработка тапов - это уже есть в текущей `0.0.1` версии Nanc. Пока все, что связано с логикой, довольно просто - реализована поддержка взаимодействия посредством нажатий на элементы и обработка соответствующих событий в вашем `.dart` коде, написанном заранее, но со временем эта часть Nanc будет серьезно расширяться, позволяя вам писать логику на Dart прямо в браузере и работать она будет и в вашем приложении. Подход к обработке пользовательских нажатий заключается в следующем - вы можете определить перечень "действий", которые может совершить пользователь в вашем приложении. К примеру - открытие внутреннего экрана в приложении, переход по внешней ссылке, отображение `SnackBar`, показ модального окна и много чего еще, и заранее создать обработчик класса подобных действий. А затем использовать это действие любым образом в Nanc. Для более подробной информации об обработке событий посмотрите документацию к виджету `InkWell` в Nanc. <iframe width="800" height="600" src="https://www.youtube.com/embed/r5jOdwJkN6Y" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> ## Convenience В Nanc есть встроенный XML-редактор, однако, он не очень удобен. В нем (пока) нет поиска, он не очень быстрый при большом объеме кода и в нем нет автодополнения. Как с этим жить? Например - позволить пользователю использовать его любимую IDE и в реальном времени наблюдать изменения в Nanc. Сейчас покажу как. <iframe width="800" height="600" src="https://www.youtube.com/embed/IN5bMDR9rgU" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> > Вот бы можно было так и на хабре - а то я уже задо*лбался исправлять и добавлять текст во время вычитки тут и в своем редакторе статьи А это Web (с которым вам и предстоит играть): <iframe width="800" height="600" src="https://www.youtube.com/embed/YULbCflmMKA" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> > В будущем будет добавлена поддержка автодополнений, возможно в отдаленном...я пытался в XML Schema, потратил несколько дней, но пока не смог 🤷🏼‍♂️ ## Performance Отдельно хотелось бы упомянуть про производительность (отрисовки интерфейса из XML на мобильных девайсах). Если кратко - она аналогична производительности самого Flutter, без какого-либо оверхеда. На текущий момент "экран" представляет из себя лениво отрисовывающийся список виджетов (SliverList), создающихся асинхронно. Немного позднее данная реализация будет доработана, чтобы виджеты начали отрисовываться также асинхронно, но по очереди, таким образом время, требуемое на показ контента будет равно времени, требуемому для отрисовки самого первого виджета, описанного в XML. # Nanc demo apps ## Общее описание Для демонстрации возможностей был создан публичный набор демо-приложений, которые показывают, чего можно достичь с помощью Nanc прямо сейчас. Это клиентское приложение на Android и Web (последнее временно играет и роль приложения для iOS). А также CMS-приложение Nanc. Подробнее о них ниже. ## Links - [Web Client](https://client.nanc.io) - [Android Client](https://play.google.com/store/apps/details?id=com.indieloper.nanc_client) - [Nanc CMS](https://admin.nanc.io) ## Client Client - это клиентское демо-приложение, использующее одну единственную библиотеку из nanc-экосистемы. Эта библиотека позволяет преобразовать XML в интерфейс приложения на Flutter. В этом приложении всего один экран, созданный полностью в Nanc и его можно обновлять как угодно и в любой момент времени без сторов. Справа снизу есть кнопка с иконкой подключения - она отвечает за подключение к [демо-CMS](https://admin.nanc.io). Подробнее о том, что это за "подключение" будет ниже. ## Admin Admin - это Nanc-CMS демо-приложение, с дополнительно внедренным слоем логики, обеспечивающим возможность синхронизации с клиентами (подробнее о подключении ниже). В демо-приложении Nanc-CMS в качестве "бэкенда" выступает сам браузер пользователя и его localStorage. Все, что вы добавите или измените - будет храниться только в вашем браузере. В Nanc-CMS вы можете изменять / создавать / удалять данные, относящиеся к существующим моделям (вы их увидите), а также - вы можете создавать посредством интерфейса свои собственные модели и делать с ними все тоже самое. В качестве SQL-репрезентации моделей данных, используемых при создании данного демо, можно ориентироваться на следующий скриншот: ![SQL-representation](https://i.ibb.co/JQyVhRW/database-example.png) ## Connection manager / "Подключение" Этот раздел относится исключительно к логике "демо" в клиентском и CMS-приложениях. И реализован он был, чтобы просимулировать опыт взаимодействия с Nanc и процесс обновления клиента. Но обо всем по порядку. В реальном production-проекте вы могли бы использовать Nanc следующим образом - развернуть где-нибудь статическое приложение Nanc CMS, с реализованными API-сервисами. Оно бы общалось с вашим бэкендом, а вы использовали Nanc на свой вкус и цвет. Ваше приложение содержит одну библиотеку из nanc-экосистемы, позволяющую отрисовать интерфейс. Вы сделали обновление - приложение загрузило новый код из вашего бэкенда, обновилось - все счастливы и довольны. Для показа этой модели в действии реализовано все тоже самое, но в упрощенном виде: Nanc CMS существует как статика, лежащая на github pages и вы можете ей пользоваться так же, как "в реальной жизни", но в качестве бэкенда выступает ваш браузер. То есть интерфейсы API были реализованы таким образом, что "ходят в сеть" они - в браузерный localStorage. С этой частью завершили, но еще есть мобильное приложение, которое должно как-то показать вам процесс "обновления". Что же, тут то и нужно "подключение". Если кратко - вы можете установить прямое соединение между любым клиентским демо-приложением Nanc и любым CMS демо-приложением Nanc. Для этого вам нужно в CMS нажать кнопку справа снизу с иконкой QR-кода. В появившемся модальном окне вы увидите QR-код. Дальше у вас есть два стула - вы можете отсканировать этот код с помощью мобильного (или браузерного) клиентского приложения, нажав в нем похожую кнопку справа снизу, тогда подключение установится автоматически. Либо вы можете нажать по QR-коду, и необходимые, для соединения, данные будут скопированы в буфер обмена. Затем эти данные вы должны будете вставить в поле для ввода в мобильном приложении, и подключиться нажатием кнопки. Когда соединение будет установлено - вы поймете сами. После этого вы сможете делать все, что хотите, с существующей страницей **Landing Page**, и видеть изменения в реальном времени (после сохранения) - в мобильном приложении. Но вы не ограничены только **Landing Page**. Вы можете прямо в браузере создавать любые новые модели, наполнять их контентом, и если в ваших моделях будет поле для описания интерфейса (тип Screen) - то при сохранении таких сущностей, вы также увидите результат в приложении - экран из новой модели заменит существующий экран приложения. Единственный момент - так как клиентское приложение не знает, какого типа то или иное поле вашей свеже-созданной записи, то в нем заранее прописаны возможные идентификаторы, которые, ожидается, и будут ScreenField. Поэтому, если вы захотите создать экран полностью с нуля и отобразить его в приложении, то используйте в качестве значения поля IdField что-то из следующего списка: - screen - ui - page - interface - markup - view Если вы что-то сломаете - просто сбросьте данные сайта admin.nanc.io (Chrome: F12 -> Application -> Application -> Storage -> Clear Site Data), ну и при повторном открытии клиентского приложения оно всегда будет загружать актуальный код экрана, созданный для этого демо. *(Код из вашего браузера будет загружен, только если вы подключитесь)* <iframe width="800" height="600" src="https://www.youtube.com/embed/Hu5S4kC84qY" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> И, напоследок, небольшой пример с созданием новой страницы новой модели, содержащей ScreenField, и отрисовкой его в приложении: <iframe width="800" height="600" src="https://www.youtube.com/embed/8zq0bRa7Tv4" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen > </iframe> # What's next? Публичное демо готово. Вводная статья написана. Дальнейшие планы касательно Nanc - завершить функциональную целостность интерфейсного подхода к созданию моделей, сделав возможным конфигурирование всех полей - Enum, Selector и MultiSelector. Пофиксить известные баги, вроде изменения положения элементов в StructureField. Затем "блаблабла", а потом "то то то". Бэклога хватит на ближайшие пол года, как минимум, но дальнейшая модель расширения функционала будет строиться на потребностях клиентов, поэтому если у вас есть идеи / критика / нашли баг _(а их тут навалом)_ / что-то еще - заполните форму, ссылка на которую доступна в клиентском демо-приложении. Если вас заинтересовали возможности Nanc, и вы заинтересованы в сотрудничестве - тоже заполните форму и мы обязательно пообщаемся.