картинка для привлечения внимания. чорт. надеюсь никто не читает альты..
Начало пути капитана $mol'a! Шта?..


Здравствуйте, меня не зовут Дмитрий Карловский и я… решил написать простенькие заметки на $mol в несколько итераций:


  1. Настраиваемся
  2. Возводим скелет
  3. Сохраняемся
  4. Шаримся
  5. Пакуемся

0. Настраиваем рабочее окружение MAM


парой команд — это делается один раз под все проекты:


git clone https://github.com/hyoo-ru/mam.git
cd mam
npm install

Запускаем дев сервер:


npm start

Он заиграет на 9080 порту http://127.0.0.1:9080, а нам можно начинать смолить..


Возводим скелет


Создаём наш проект: в склонированной папке mam создаем папки habr/notes


В нём создём точку входа habr/notes/index.html:


<!doctype html>
<html mol_view_root>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">
    </head>
    <body mol_view_root>
        <div mol_view_root="$habr_notes"></div>
        <script src="web.js" charset="utf-8"></script>
    </body>
</html>

Заметили $habr_notes? Тут везде fqn обращения (имя = путь), а в mol_view_root мы указываем наше приложение. Точнее, компонент — здесь это одно и то же по смыслу, только мы можем вкладывать компоненты друг в друга и делегировать/рекомпозировать/хакать их свойства между собой.


Что же, создадим сам компонент habr/notes/view.tree:


- это комментарий, ах да, следим за табами
$habr_notes $mol_page

Шпаргалка по синтаксису и страшная статья о view.tree


В браузере можем перейти по адресу http://127.0.0.1:9080/habr/notes и увидим зачаток нашего приложения:


*гифка*


Придадим форму, подправив файл view.tree:


- мы отнаследовали $habr_notes от $mol_page
$habr_notes $mol_page
    - в свойство body мы положили массив /
    body /
        - в этот массив мы вложили компонент $mol_textarea и назвали его Input
        <= Input $mol_textarea

О, теперь у нас появилось текстовое поле ввода:


*гифка*


Но наш введенный текст теряется при обновлении страницы, давайте это изменим.


Сохраняемся...


в localStorage


Подправим наш view.tree файл до такого содержания:


$habr_notes $mol_page
    body /
        <= Input $mol_textarea
            - переопределяем наше свойство value
            value?val <=> text?val \

подробнее про свойства компонента $mol_textarea туть

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


view.tree — это DSL, транслирующийся в ts.


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


namespace $ {
    export class $habr_notes extends $mol_page {

        /**
         * ```tree
         * body / <= input
         * ```
         */
        body() {
            return [
                this.Input()
            ] as readonly any[]
        }

        /**
         * ```tree
         * text?val \
         * ```
         */
        @ $mol_mem
        text(val?: any) {
            if ( val !== undefined ) return val as never
            return ""
        }

        /**
         * ```tree
         * Input $mol_textarea value?val <=> text?val
         * ```
         */
        @ $mol_mem
        Input() {
            const obj = new this.$.$mol_textarea()

            obj.value = (val?: any) => this.text(val)

            return obj
        }
    }

}

Вы можете сами поиграться в песочнице.


Зачем он нужен? Для разделения мух и котлет.


С помощью view.tree мы декларативно описываем наш интерфейс.
С помощью view.tree.ts мы императивно описываем работу самой логики.


Из view.tree сборщик сгенерировал шаблонный код в файл habr/notes/-view.tree/view.tree.ts.


При обновлении view.tree файла, файл выше обновляется сам.


Зачем он нужен? Для переопределения!


Можем заметить, что код выше сгенерировался в неймспейсе $.


Свою логику мы будем описывать в неймспейсе $.$$ — то есть, "перекрывая" своей логикой.


Вернёмся же к нашему коду.
Создаём файл habr/notes/view.tree.ts:


namespace $.$$ {
    export class $habr_notes extends $.$habr_notes {
        @ $mol_mem
        text(val?: any) {
            if ( val !== undefined ) return val as never
            return ""
        }
    }
}

Тут я перенёс text(val?: any) — функция-заглушка, сгенерированная из view.tree файла с навешенным декоратором @ $mol_mem (от memoization)


Это канал — он возвращает значение, при вызове его без параметра, а при вызове с параметром он указывается в качестве значения.


Почему оно работает?


Мы открыли наше приложение, поле ввода запросило значение из функции text, функция text вернуло пустую строку.


Мы ввели первый символ, в функцию text передалась нажатая клавиша, функция text сохранила её.


Мы вводим последующие символы, они запоминаются и результат функции text возвращается в поле ввода.


Ага, значит сюда и можно подвязать сохранение данных в localStorage.


В $mol есть модуль $mol_state_local — реактивная обертка над localStorage — ей и воспользуемся:


namespace $.$$ {
    export class $habr_notes extends $.$habr_notes {
        @ $mol_mem
        text(val?: any) {
            return $mol_state_local.value('note', val) || ""
        }
    }
}

Проверяем… работает! После перезагрузки страницы наш введённый текст сохраняется.


**гифка**


Я создал репозиторий с веткой stage-1, действия выше воспроизведены в нём — можно сравниться.


Что же, осталось научиться шариться данными!


Собственно,


Шаримся


заметками, используя децентрализованную криптографическую систему, ну а почему нет
¯\(ツ)


Поправим view.tree файл:


$habr_notes $mol_page
    - через синк клиент мы обмениваемся данными в сети
    yard $hyoo_sync_client
    tools /
        - через модуль онлайна мы подключаемся к пирам и синхронизируемся
        <= Online $hyoo_sync_online
            yard <= yard
    body /
        <= Input $mol_textarea
            value?val <=> text?val \
            enabled <= i_have_can_mod false

Опишем логику view.tree.ts:


namespace $.$$ {
    export class $habr_notes extends $.$habr_notes {
        @$mol_mem
        text( val?: any ) {
            return this.store().str( val ) || ""
        }

        @$mol_mem
        store() {
            // через модуль клиента мы обращаемся к хранилищу (миру),
            // используем тип $hyoo_crowd_reg - он позволяет атомарно хранить значения (строки, числа, булы)
            // запрашиваем участок с указанным id
            return this.yard().world().Fund( $hyoo_crowd_reg ).Item( this.current_note_id() )
        }

        @$mol_mem
        current_note_id() {
            // мы считываем id заметки из урла, или, если пустой, указываем новый свой
            const id = this.$.$mol_state_arg.value( '' ) || this.yard().land_grab().id()
            // задаём в урл текущий id - чтобы мы могли его сразу же скопировать
            this.$.$mol_state_arg.value( '', id )
            return id as $mol_int62_string
        }

        @$mol_mem
        i_have_can_mod() {
            // проверка наличия прав на редактирование
            return this.yard().land( this.current_note_id() ).allowed_mod()
        }
    }
}

Вот как это выглядит:


ля, работает


Для сверки, ветка stage-2.


А может вам рюшечки?


Добавим поддержку тем тройкой строк кода:


$habr_notes $mol_page
    plugins /
        - включаем поддержку смены тем
        <= Theme $mol_theme_auto
    yard $hyoo_sync_client
    tools /
        <= Online $hyoo_sync_online
            yard <= yard
        - добавляем кнопку-переключалку
        <= Lighter $mol_lights_toggle
    body /
        <= Input $mol_textarea
            value?val <=> text?val \
            enabled <= i_have_can_mod false

Вот так это выглядит:



Или ехать?


А может сделаем наше pwa offline-first?


Пфф: создаем в той же директории файл view.meta.tree:


include \/mol/offline/install

Одной строчкой мы подключили Service Worker, который закеширует наше приложение и позволит работать ему при отсутствии интернетов.


Зачем? CROWD, который мы используем, автоматически синхронизируется с внешним миром при потери и появлении интернета.


Для сверки, ветка stage-3.


Пакуемся


мы командой:


npm start habr/notes

Наш статический бандл расположится в папке habr/notes/-/ — его и можно хостить.


В ходе всех вышеописанных шагов можно заметить появившиеся папки:


  • -/ — наш бандл
  • -view.tree/ — наш переопределяемый код

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


Например, файл .gitignore будет таков:


-*

Отвлеклись, задеплоимся на какой-нибдь фапхаб с возможностью хостить статические странички — тот же Github Pages.


Выгрузим содержимое папки - в ветку gh-pages и можем лицезреть результаты наших работ по ссылке: https://koplenov.github.io/habr-notes/#!=sxktck_j3e02v


Схожее по теме:



Ссылка на доку туть, а мы обитаем туть..

Комментарии (4)