Как стать автором
Обновить
0
0
Кирилл Саксин @saksmt

Scala/Kotlin/Java/%JVM_LANG_NAME% разработчик

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

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


Я привёл код, который генерируется из TSX. И реакта там нет, если вы не заметили.

Не суть, главное, что у JSX есть интерфейс для интеграции. + где-то на просторах интернета была статья о использовании JSX как я описал, вот только ссылочка потерялась.


Стандартными возможностями языка TypeScript нельзя иерархически объявлять свойства

Проявите фантазию:


interface Attribute {
    name: String
    value: String | Number
}

type Content = Element | String

interface Element {
    attributes: Array<Attribute>
    children: Array<Content>
}

type ElementConstructor = (attrs: Array<Attribute>, children: Array<Content>) => Element
type DSLContent = Attribute | Content

function simpleProp(key: String, value: String | Number): Attribute {
}

function buildElement(construct: ElementConstructor, ...content: DSLContent[]): Element {
    return construct(
        <Attribute[]> content.filter(it => it instanceof Attribute),
        <Content[]> content.filter(it => it instanceof Content)
    );
}

function curry<A, BA, B extends Array<BA>, R>(source: (A, B) => R): (A) => (...BA) => R {
    return a => b => source(a, b)
}

function myComponent(): Element {
}
function myBaseComponent(attrs: Attribute[], content: Content[]): Element {}

var dsl = curry<ElementConstructor, DSLContent, Array<DSLContent>, Element>(buildElement)

dsl(myBaseComponent)(
    simpleProp("id", "myBaseComponent"),

    myComponent(),
    "hello",
    dsl(myBaseComponent)(
        simpleProp("id", "myBaseComponent2")
    ),
    myComponent()
)

P.S. В typescript не очень-то умею.


Идея $mol в том, чтобы программисту было легче написать "нормальный" код, чем "ненормальный", без какого-то "особого проектирования", которое усложняет, раздумает и замедляет код.

Не бывает "особого" или не "особого" проектирования, оно либо есть, либо код загнивает. + у каждой библиотеки есть своя философия и если её не придерживаться то непременно выйдет говнокод.


Точно. Что ж, вам своё лучше не пилить. Воспользуйтесь моим ;-)

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


О том и речь, что мало кто способен на реакте хорошо реализовать даже такой простой компонент

Вы путаете, там реализовывали не простые компоненты, а универсальные вундервафли, так что не в счёт.


Всё честно. Компонент умеет ровно то, что необходимо и ничего лишнего.

В таком случае не честно выбирать LOC метрику для сравнения. Хотя можете оставить LOC, только примените его для сравнения покрытия.


Ок, можно. Но смысла всё-равно нет.

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


Эти соглашения создают определённые проблемы. Можно лепить костыли типа конвертации и поддержки IDE, а можно использовать другие соглашения, которым костыли не требуются.

И потом упорно объяснять, что вы не верблюд когда у вас спрашивают "чем же вас не устроили общепринятые стандарты?"


Что вы там и куда в css собираетесь транспилировать?

Я про JS


О том и речь, поэтому и используется суфикс "er".

Это-то тут причём? Если все мои классы вдруг начнут называться Classer я наверно приму решение о самовыпиле из мира сего, собственно названий Viewer для всех вьюх тоже будет достаточно.


Преувеличение. У вас похоже получился слишком специфичный компонент.

Для этой области вполне обычное дело.


Пример css селектора.

Допустим есть вот такой компонент — search.customers.CustomersPaneComponent, для него селектор мог бы быть например таким — search-customers--customers-pane или seach-customers--customersPane

Не тот случай, это даже "вариантом" назвать нельзя.

Простой пример — вы реализуете компонент "диалог", который по хорошему должен инкапсулировать в себе "панель", но с вашим подходом "по концепту реакта" вы не сможете абстрагироваться от типа панели, так ка на вход принимаете "компонент подвала для такой-то панели".

Крайне странный пример, который не показывает в чём же у меня в этой ситуации возникнут проблемы. Особенно при условии, что футер и шапка поумолчанию null и не выводятся.


Я в предыдущем сообщении привёл привёл код, который генерируется. Зачем вы фантазируете?

Вы привели код, который генерирует реакт+jsx, а не jsx.


Я его аргументировал. А у вас есть что возразить, кроме перехода на личности?

Вы его не аргументировали. "Мне не нравится" и "Слишком многословно" — это не аргументы в пользу "XML не наглядный".


Стандартными возможностями языка TypeScript нельзя иерархически объявлять свойства — только в плоском виде.

Вложенные вызовы, лямбды, ...


Через view.tree это реализуется элементарно

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


Откуда эта странная любовь к костылям к сторонним решениям, вместо написания своего без костылей?

Вы точно в продакшне работаете? Своё — это, за редким исключением, велосипед на костылях, который требует поддержки и покрытия.


Если перегружается компонент, который его содержит, то странно ожидать, что вложенные компоненты

Тем не менее это будет наверно самая популярная ошибка.


У меня получились несколько иные выводы

Которые опять-таки не говорят о ущербности реакта, а разве что о ущербности разработчиков конкретных библиотек + у вас там совершенно не честное сравнение вашего компонента, который почти ничего не умеет, и сферических космолётов в вакууме.


В идентификаторах её нельзя использовать.

Консоль огнелиса:


>> var ё="можно"
<< undefined
>> ё
<< "можно"

А зачем вводить дополнительные правила, если их можно не вводить? Только потому, что вы привыкли в-css-писать-имена-классов-через-дефисы, а.в.javascript.через.Точки?

Именно! Потому что эти символы в этих языках имеют смысл и не пересекаются с уже установленными соглашениями.


Аннотации тут ни чем не помогут. Да и кроме TS есть и другие языки.

@Component
class MyComponent {}

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


Тогда незачем тавтология

Не слишком удобно, когда каждый класс вьюхи называется View. С тем же успехом можно было бы назвать вообще все классы Class и просто класть в разные пакеты.


И что тут сомнительного? Ну, кроме того, что глубина вложенности скорее всего преувеличена. И что такое SID?

Сомнительно, что человек в здравом уме захочет иметь такие имена классов. А пример — не преувеличение. SID — что-то вроде унифицированной базовой модели любой биллинговой системы.


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

Вам примеры FQCN (Fully Qualified Class Name) нужны?

Венгерская нотация уже давно умерла (с появлением нормальных IDE), да и то, что вы тут описываете — не венгерская нотация.

То, что вы не правильно поняли концепт реакта не говорит о том, что он плохо расширяется. Это говорит лишь о том, что если вы пилите компонент панели, то всё, что она о себе должна знать — это то, что у неё возможно (nullable) есть шапка, тело и футер, а вот что в них будет решат уже они сами (собственно в моём примере на scala-tags+udash это и реализовано).


К TSX это не прикрутить

Вы в этом уверены? Насколько я понял tsx/jsx транслируется примерно в такие вызовы (псевдокод): tagName[T](children: List[T], attributes: Map[String, Object]): T


Но как я уже говорил, наглядность у XML хуже, а кода больше

Это лично ваше мнение.


view.tree — и есть DSL

view-tree — это DSL на базе самописного формата. Я же имел ввиду именно DSL на базе стандартных возможностей языка, хоть в JS это и не популярно.


Во view.tree вы можете перегрузить как сам блок (bodier), так и только лишь его содержимое (body). Во втором случае будет сгенерирован дефолтный bodier, в который и будет вставлено содержимое.

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


Это вообще шаблонизаторы

Да, но их можно было бы преобразовать при очень большом желании. + nunjucks умеет в транспиляцию насколько я помню.


Единственное сходство с ExtJS — и тот и другой предоставляют базовый набор готовых компонент. Это делает любой компонентный фреймворк.

Я бы сказал расширенный набор компонент. И на самом деле таких фремворков сейчас как-то не видно. В лучшем случае есть набор не связанных сторонних компонент от сомнительных вендоров. У вас же виден прицел на быстрое клепание enterprise-форм, что сильно выделяет среди остальных (в хорошем смысле)


И что же там может пойти не так, например?

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


Иначе у вас неизбежно будет получаться вот такой вот говнокод

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


Это аналоги десятистрочного $mol_pager на реакте.
Разве что первый.

Символ подчёркивания — единственный символ, который можно одинаково использовать хоть в JS, хоть в CSS, хоть ещё где

"ё" тоже можно одинаково везде использовать, но-таки не стоит. Лучше ввести несложные правила замены, тогда семантически везде будет одно и то же.


Чтобы минимизировать ложноположительные срабатывания. Токены, начинающиеся на $, достаточно уникальны.

В ts же вроде как аннотации есть, зачем тогда так извращаться?


Уже лучше, но как-то костыльно — для каждого класса отдельный неймспейс

Ровно до тех пор, пока не появятся другие классы в неймспейсе, например, CustomerDao и CustomerView в mycompany.search.customer


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

Сомнительный момент для, например, mycompany_search_customer_results_card_subdivision_account_identification_place_registration и человека, который разрабатывает форму лицевого счёта. И поверьте, в SID бывает ещё и не такое...


но какой должен сгенерироваться из них класс/атрибут для стилизации в css?

FQCN, до тех пор, пока не задано иное явно.

Про первые четыре абзаца:

Вы JSX видели? Видели чем он по факту является? Так вот я предлагаю вам не городить свои велосипеды и не писать свой парсер (который к слову будет иметь баги, каким бы крутым разработчиком вы не были), а взять уже готовое решение — JSX, mustache, nunjucks или в крайнем случае написать свой DSL (не уверен на счёт TS, но в котлине или скале оно делается совсем не сложно)

>> Вот вы жалуетесь, что реализация подкачала. Но что вы предлагаете? Перевести весь код на scala?
Боже упаси, нет конечно. Не думайте, что я упоротый и пытаюсь воткнуть любимую скалу повсеместно. Это были лишь примеры того, что не обязательно велосипедировать и вполне можно обойтись простеньким DSL-ем.

>> но по факту получить лишь вопросы в духе «ну и зачем ещё один велосипед, который делает то же, что и реакт?!»?
Сомневаюсь, что кто-либо будет задавать такие вопросы. В конце концов вы пилите один из немногих на самом деле реактивных фреймворков + насколько я понял ваши идеи — это сотворить современный extJS «с блекджеком...»

>> В вашем коде потерялись следующие свойства: head, body, foot, позволяющие изменить содержимое соответствующих блоков, не меняя сами блоки
Они через конструктор передаются.

>> Кроме того, потерялся вложенный блок titler, который по умолчанию выводится в header, и который выводит внутри себя свойство title, которое есть у всех компонент.
Представьте себе ситуацию, когда кто-то изменит одновременно header, titler и title и после этого начнёт удивляться что же пошло не так. Нельзя давать слишком много гибкости. Вилкой тоже можно суп съесть, но зачем.

>> Касательно сборщика
Вам не кажется более логичным использовать как разделитель точку? И это всё равно не объясняет зачем вам "$"

>> $mol.gridView и $mol.grid.row.RowView опять же имеют разные пространства имён. Тогда уж $mol.grid.view и $mol.grid.row.view
$mol.grid.GridView и $mol.grid.row.RowView
>> Думаю вам стоит несколько расширить свой кругозор.

Дело не в конкретном числе, а именно в малом количестве — я не просто так подметил, что в реальности это число ближе к 4. В целом это больше говорит о KISS. Т.е. никто не будет на самом деле сидеть и считать «а не привысило ли число абстрактной ерунды в в этой штуковине число 7?». Кстати не очень понимаю зачем в той статье упомянуты дизайнеры — мне всегда казалось, что у них число 3 более важно (максимальное число переходов, максимальное число шрифтов/цветов...)

>> Нет никакой рациональной причины фрагментировать сообщество.
У вас нет выбора «фрагментировать своё сообщество или нет» у вас выбор «будет у меня фрагментированное сообщество или его не будет совсем». Сомневаюсь что найдётся много разработчиков (не студентов, которым время девать некуда и не юниоров, которым надо просто «побольше выучить, чтоб казаться про»), которые бы стали копать вашу документацию и разбираться в тонкостях новых способов разметки, если им нужно решение здесь и сейчас, это даже для нелегаси проектов не прокатит.

>> Это нормально, что один и тот же символ в разных контекстах вызывает разные ассоциации.
Нет, это скорее исключение. "!=" везде «не равно» (haskell не в счёт) и "+" везде плюс. Не надо усложнять и так не простую жизнь разработчика новыми значениями для привычных вещей.

>> Целью статьи было поделиться свежими идеями
Я в самом первом комментарии написал, что идея хороша, а вот реализация подкачала.

>> Всегда найдутся те, кто не поняли, не привыкли, не заинтересовались
Естественно. Вопрос в количестве — и чем больше нового вы привносите (прямо скажем за зря) тем больше их будет.

>> $ используется для того, чтобы обозначить глобальное пространство имён. Именно благодаря ему, сборщик быстро находит зависимости.
Вы сейчас про typescript или про префиксы своих компонентов пишете?

>> Для $mol разработчика, в коде view.tree тоже нет ничего нового
Есть принципиальная разница: для scala-разработчика в udash и Binding.scala нет кардинально нового, а для ts-разработчика (и тем более js) в $mol сликом много нового.

>> Но если view.tree можно изучить за пару часов вдоль и поперёк, то на скалу придётся потратить существенно больше времени
Зависит от того насколько глубоко нужно изучить скалу и что уже знает изучающий.

>> Подозреваю, вы не дочитали до того момента, где объясняется, почему вместо описания класса на TypeScript был введён специальный язык.
Либо я плохо искал, либо у вас нет этого объяснения. У вас есть только сравнение с xml-подобным синтаксисом. Не самое убедительное кстати.

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

>> Попробуйте реализовать аналог
https://gist.github.com/anonymous/17043c00b00117eb7af266df9cd7b802:

/**
* param header Reactive property binding
* param content Reactive property binding
* param footer Reactive property binding
* param attrs Attributes
*/
case class Page(
private val header: Property[Element] = Property[Element],
private val content: Property[Element] = Property[Element],
private val footer: Property[Element] = Property[Element],
private val attrs: Modifier*
) {
def render = div(attrs: _*)(
header,
content,
footer
)

def apply(
header: Property[Element] = this.header,
content: Property[Element] = this.content,
footer: Property[Element] = this.footer,
modifiers: Modifier* = this.attrs
) = copy(
header = header,
content = content,
footer = footer,
modifiers = modifiers
)
}

val myContent = Property(p(«hello»).render)
val mainPage = Page(style := MainPage.styles)(
header = Property(h2(«Hello»))
content = myContent
)

// application in body

body(
onclick :+= { myContent.set(p(«world»)) }
mainPage.render
)

>> Вариант $mol.rowInGridView плох тем, что его имя вылезает за пределы модуля $mol.gridView.
$mol.grid.row.RowView
Сильно в этом сомневаюсь, особенно учитывая скорость работы.
В этом я не уверен. И даже если это так, то там явно есть «компонент, который загружает данные»
Эта «байка» к месту ровно всегда и не пытайтесь искать себе оправдания.

Сообщество сойдётся на максимум 3 стандартах из которых мейнтейнеру и нужно выбирать, а не думать, что у кого-то "#" начнёт ассоциироваться с идентификатором, а не комментарием. Касательно остальных ваших я согласен разве что с экранированием, да и то относительно.

Как вы думаете почему я первую половину статьи прочитал, а остальную бегло просмотрел и начал «выискивать»? Всегда задавайте себе такой вопрос, когда кто-либо относительно аргументировано начинает критиковать — обычно это значит, что вы что-то не учли или не просчитали либо при написании/проектировании, либо при выборе target группы. Так вот, начал я к "$" докапываться потому что у меня возникло ощущение, что я читаю статью о php/bash/perl, а не о typescript. А про спецсимволы я свою позицию уже объяснил.

Вам это не говорит о том, что код не понятен? Кхм, мой пример имеет в разы большую гибкость, вплоть до выбора типа элемента + оба возможных метода расширения функционала: наследование и композиция элементов. Уж не знаю как вы считали (генерики с лямбдами учли небось?), но для скала разработчика там всего 2 спец символа и те вполне понятны, особенно если уточнить, что ":" просто суффикс, ибо "=" не перегрузить, а именованых вараргов в отличие от питона не завезли. Касательно конструкций — там лишь наследование, генерики, свойства, методы и лямбды — ничего сверхестественного или непонятного. И да, я считаю эту «портянку» ЗНАЧИТЕЛЬНО более предпочтительной, а для тех, кому трудновато пока освоиться с нестандартным синтаксисом, но очень понравилась идея — рекомендую Binding.scala, что выглядит как реакт, с той лишь разницей, что оно действительно реактивно.

И вот моя попытка номер 2 в осознании вашего кода (https://gist.github.com/anonymous/d56c6665ab6fbea6c162f2f76abbc983):

// literally rewrote
def clickableElement(text: Modifier): TypedTag[Button] =
button(
enabled := true,
onclick := { false },
disabled := false,
tabIndex := 0
)(text)

// More generic way:
def button(text: => Modifier): TypedTag[Button] =
typedTag[Button](«button»)(
enabled := true,
onclick := { false },
disabled := false,
tabIndex := 0
)(text)

// My thoughts about what it was…
class Button extends View {
protected def enabled = true
protected def disabled = false
protected def clickHandler: (Event) => Boolean = { e => false }
protected def tabIndex = 0

abstract protected def text: Modifier

def render = button(
enabled := { this.enabled || !disabled }
onclick := clickHandler
tabIndex := { tabIndex }
)
}

>> «clicker» в данном случае — сокращение от «clickable presenter»…
Вы уж определитесь с тем, что это такое: вью, презентер (кстати рекурсивный каламбур по вашей логике получается :) ) или компонент и с тем, для чего вы ставите эти суффиксы как следствие. Я вам предложил суффикс который бы отделял компоненты от полей. И чем вас так не устроил вариант "$mol.rowInGridView"
Гитхаб вроде вполне себе SPA, если вы это подразумеваете.
То есть по вашему хаб/лор/гитхаб — веб-страницы и они «данные, которые дополняются интерфейсом», а не «интерфейс, который загружает данные»? В таком случае я окончательно потерял нить логики.
Это действительно так странно :)
«Всего 8» — это не «всего». Запомните магическое число 7 — это максимум однотипной не очень понятной ерунды которую среднестатистический человек может держать в голове, соответственно идеально — 3-4. Они не наглядны, ибо нигде раньше в таком контексте не были использованы. Возможность писать «и так и сяк» должна быть по крайней мере до тех пор пока сообщество (а не вы, что важно) не примет один из стандартов.

>> Нечто такое `$.$` писать не нужно.

Оно в статье есть? — Есть. Значит-таки иногда оказывается нужно.

>> Код на некогда популярном jQuery ими утыкан чуть менее, чем полностью.

И вы хотите сказать, что это хорошо?

>> Впрочем, если отставить в сторону эти «фу, мне не нравится, я так не привык», то как бы вы реализовали view.tree?

Да хоть бы и так (https://gist.github.com/anonymous/d05bf00e33fa6e23141178e0aa524cea):
```scala
class MyView[ElementType](val e: TypedTag[ElementType], val clickHandler: (Event) => Any)
extends View
with MySuperBehavior {

val child = div()

override lazy val getTemplate = {
e(
enabled := true,
onclick := clickHandler,
onmouseover :+= { println(s«Someone moved mouse over ${e.tagName}») }
// In reality would be some sort of template method, so there won't be any `getTemplate` methods
getMySuperModifiers()
)(
p(«HEADER PART»),
child,
p(«FOOTER PART»)
)
}

override def renderChild(view: View): Unit = {
child.children.foreach(grand => child.removeChild(grand))
view.getTemplate.applyTo(child)
}
}
```
Пример, очевидно, не рабочий. Если хочется посмотреть на подобную штуку поподробнее — google://udash+scala.js

>> Это соглашение об именовании такое — заканчивать презентеры на «er», что даёт похожие на англоязычные имена.

Эти имена не правильны, большинства этих английских слов либо нет, либо они значат не то, что вы ожидаете. Лучше бы уж тогда по Java косили с повсеместным "-able" оно хотя бы переводится более-менее адекватно, да и по смыслу значительно больше подходит. И ещё раз для закрепления: clicker — «тот, кто кликает», clickable — «то, что по чему можно кликнуть».
Вполне достаточно для: хабра, LOR-а. Почти достаточно для гитхаба. И это если не сильно задумываться
На хабре — нет, на гитхабе оно тоже не совсем полностью js. Там судя по вёрстке js только «user-content-» префикс добавляет.

Зато вполне есть «comment_9889944» (ваш комментарий) и оно действительно работает как якорь без всякого js
Ровно весь формат Tree, ровно каждый ваш идентификатор начинается с "$" это не говоря уже о «глобальном» пространстве имён, которое тоже "$", что в результате заставляет писать нечто такое: `$.$`. Основная проблема в том, что все эти спецсимволы новы для того, кто начинает пользоваться вашим фреймворком. В той же скале почти для каждого символа такого рода есть вполне человеческое имя метода, да и каждый новый фреймворк обычно не привносит более 1 метода названного через спецсимвол на десяток классов + те, что есть уже давно зафиксированы в сообществе. Например, есть `::` для слияния списков (и их разбора, но это отдельная тема), есть `->` для создания кортежей и есть фреймворк akka, который из часто используемого привносит только `!` (отправить сообщение) и `?` («спросить» сообщение). При этом для кортежей можно использовать `Tuple2(a, b)`, для отправки сообщения `tell(message)`, для «вопроса» — `ask`, собственно для списков тоже есть метод (вот только я уже забыл его название). В вашем же случае вы просто вбрасываете охапку этих спецсимволов без каких-либо альтернатив и надеетесь, что кому-то будет легко это запомнить.

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

UPD: и хабру тоже (якорь в комментах)
Ну с оговорками конечно, но в целом да. Behavior — принцип, но не определяющая. И я тут скорее о скале говорил нежели о жс.

А за ссылочку спасибо — почитаем-с. Не сталкивался я ни с чем в хаскеле приближенным к реальности, хоть и делал набеги :)

P.S. Зря вообще написал. Не думаю что имеет смысл продолжать эту ветку — не будем заранее отпугивать тех, кто совершенно не в теме.
Rx — таки FRP, правда смапленное на понятие потоков. По определению FRP — асинхронность + event-ы + функциональные операции (map/filter/fold/flatMap (bind))

Информация

В рейтинге
Не участвует
Откуда
Санкт-Петербург, Санкт-Петербург и область, Россия
Дата рождения
Зарегистрирован
Активность