Новостное приложение: с нуля до публикации в Google Play. Часть 1

2024 ж. 14 Мам.
20 108 Рет қаралды

Разработка приложения для просмотра новостей на основе публичного API с нуля до публикации в Google Play. Процесс будет разбит на несколько стримов, но разбираем все подробно
🔗 Relocode abdev.by/awdE?/erid=LdtCKQ8Pp
🔗 Каналы "Android Broadcast" taplink.cc/android_broadcast
✉️ Написать Кирилл kirill@androidbroadcast.dev
🔗 Android Broadcast Плюс abdev.by/q2A
#кодинг #androiddev #android #программирование #kotlin #КириллРозов #андроид #androidbroadcast
0:00 Ожидание
0:31 Начало
1:21 Что делаем сегодня
2:49 Технологии
4:07 Про исходный код и полезная информация
5:45 Разбор API
8:36 Создание проекта, почему Compose
10:18 С чего начнем
11:31 Архитектура и библиотека, как повлиять на это - Boosty
12:19 Поймет ли Junior
12:50 Стэк
14:55 Настройка многомодульности
17:00 Добавление библиотек
23:42 NewsApi, начинаем создание
26:06 NewsApi, Enum для сортировки, аргументы пагинации
27:27 Про автоген по api
28:27 Удобные Jetpack аннотации
30:00 Класс для сортировки, мультикурсор
32:55 Модель возвращаемого типа
34:24 Почему везде @Serializable
35:25 Создаём модели сущностей ответа
39:21 NewsApi, инициализация
41:44 Trailing coma
42:35 NewsApi, converter factory для retrofit
44:37 Ссылка на документацию в api
45:45 Сериализация данных
55:48 Почему стоит скрывать API
56:06 Kotlin Result в возвращаемых типах Retrofit
58:30 Interceptor для api key
1:04:00 Разница между Interceptorами, настройка OkHttpClient
1:06:15 Вопросы чата
1:07:18 Как разделять api сервисы по модулям
1:08:05 Почему public поля моделей?
1:08:25 Как передать api key в Header, суть Interceptor
1:09:31 Почему не BuildSrc?
1:10:33 Кэширование данных в Базе Данных (БД)
1:11:30 БД, чистим build.gradle
1:11:51 БД подключаем Room и ksp
1:14:59 Зачем apply false в плагинах
1:16:45 БД, имплементация Room
1:20:20 БД, @Embedded для вложенных классов Entity
1:21:40 DBO и DTO для мапинга
1:23:35 БД, про кэш и key
1:26:06 БД Room Embedded
1:28:19 Ответы на вопросы зрителей
1:30:49 Как будут организованы фичи
1:32:36 Чистим build.gradle, подключаем Compose
1:33:55 Создаем входную Composable функцию для фичи
1:34:28 Получаем Jetpack View Model, корутины, бд и api модули
1:38:22 Создаем NewsViewModel, domain модель новостей
1:39:34 Создаем State класс, StateFlow через get()
1:41:57 News-data модуль для медиации api и бд
1:44:08 Доделывание БД, Dao, Clear table
1:48:05 Доделывание БД, Database
1:51:10 Room date type converter
1:53:06 Flow без suspend в dao, чистка tests
1:54:05 Имплементация медиатора news-data в виде репозитория
1:55:25 Про модели
1:57:00 UseCase для примера
1:58:10 Доработка репозитория
1:59:28 Мапперы
2:01:05 Room как api зависимость, как исправить
2:04:14 Обсуждение логики, состояние запроса из бд
2:08:06 Финал, что дальше
2:08:38 Где найти исходники?

Пікірлер
  • 🔗 Каналы "Android Broadcast" taplink.cc/android_broadcast 🔗 Boosty Android Broadcast abdev.by/Fwkx

    @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Благодарю! Великолепный контент. Очень не хватает "наставника", который мог бы вот так вот расскать и показать... Вот и приходится всегда все сам да сам, поиск решений на просторах, а как правильно - хз. Супер!

    @artemdorofeev44@artemdorofeev442 ай бұрын
  • Стрим суперовый. Надеюсь весь (будущий) плейлист останется бесплатным. Заранее спасибо за контент.

    @andrewgladkov9191@andrewgladkov91912 ай бұрын
  • Вот это очень полезный контент! Лучше простых разговоров с разработчиками без кода...

    @maksonic_official@maksonic_official2 ай бұрын
    • Спасибо

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Спасибо. Хочется продолжения

    @sovrinfo@sovrinfo5 күн бұрын
  • Комментарий для поддержки. Спасибо за то, что Вы делаете! Было бы славно увидеть продолжение

    @ALLvays@ALLvays2 ай бұрын
    • Спасибо

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Спасибо огромное, жду с нетерпением продолжения. 👍🔥

    @misterex99@misterex992 ай бұрын
  • Очень полезная серия видеороликов. Спасибо Вам большое.

    @mikhailshirokov8464@mikhailshirokov846411 күн бұрын
  • Наконец-то, я так долго ждал подобного видео, спасибо за контент))🎉

    @user-dn4xk9um3c@user-dn4xk9um3c2 ай бұрын
  • Отличный стрим, давно ждал подобного контента. Надеюсь увидеть Di в следующих частях !

    @user-xo3xs5tt4p@user-xo3xs5tt4p2 ай бұрын
  • Спасибо за видео. Интересно, жду продолжения

    @deadchannal@deadchannal2 ай бұрын
  • Супер, спасибо! Пожалуйста, продолжайте!

    @guyWithPotato@guyWithPotato2 ай бұрын
  • Спасибо! Обязательно продолжай!

    @olegkotov4630@olegkotov46302 ай бұрын
  • Обязательно продолжайте!

    @sergeikuznetsov5187@sergeikuznetsov51872 ай бұрын
  • Круто, продолжение нужно полюбому ! =)

    @user-yq6ru8nn1d@user-yq6ru8nn1d2 ай бұрын
  • Кирилл, великолепно!!! Спасибо.

    @user-qu6mb9il6w@user-qu6mb9il6w2 ай бұрын
  • Спасибо за супер полезный контент!

    @spyro2008@spyro200810 күн бұрын
  • Комментарий для поддержки и продолжения рубрики

    @maleksee@maleksee2 ай бұрын
  • Было очень интересно. Кирилл продолжай

    @artiomponomariov2213@artiomponomariov22132 ай бұрын
  • Очень круто. Хочется продолжения

    @user-xl2tx1di8r@user-xl2tx1di8r2 ай бұрын
  • Очень интересно рассказываете! Было бы здорово увидеть продолжение :)

    @Japskiddin@Japskiddin2 ай бұрын
  • Очень круто! Очень полезные Ваши стримы с кодом!

    @zoompartyru@zoompartyru2 ай бұрын
  • мой любимый формат, спасибо

    @azatsabirov863@azatsabirov8632 ай бұрын
  • Спасибо за стрим! Я только начинаю разбираться в андроид разработке. Очень хочется узнавать best-practices. Ваши видео помогают в этом!

    @user-qe5ny9hk3f@user-qe5ny9hk3f2 ай бұрын
  • Очень полезно! спасибо! и храни тебя Бог

    @nurlandroid@nurlandroidАй бұрын
  • Очень круто и полезно❤❤❤

    @user-mr2bt1no9v@user-mr2bt1no9v2 ай бұрын
  • спасибо за видео, и отдельный респект за таймкоды!

    @elpi785@elpi785Ай бұрын
  • Спасибо за контент такого формата!

    @renheyzer5125@renheyzer51252 ай бұрын
  • Огонь, хотелось бы еще!

    @user-dn1ep2kb4o@user-dn1ep2kb4o2 ай бұрын
  • Спасибо за видео, очень крутой формат!

    @user-mf6bg3rb7j@user-mf6bg3rb7jАй бұрын
  • Ждем продолжение

    @user-tj8xu4lg1w@user-tj8xu4lg1w2 ай бұрын
  • Очень полезный формат, спасибо!

    @SpatiumCat@SpatiumCat2 ай бұрын
  • Спасибо большое за ваш стрим. Это действительно очень полезно для начинающих, в том числе и для меня

    @azamatik3@azamatik3Ай бұрын
  • Спасибо. Крутяк!

    @cibofff@cibofff2 ай бұрын
  • Очень полезный и интересный стрим, спасибо!

    @user-vq8bo3ux7y@user-vq8bo3ux7y2 ай бұрын
  • Комментарий для поддержки контента, спасибо!

    @user-es8vr2gu9s@user-es8vr2gu9s2 ай бұрын
  • Классная идея, давай вторую часть )

    @JustAmalll@JustAmalll2 ай бұрын
  • Большое спасибо.

    @DiabloZq@DiabloZq2 ай бұрын
  • Благодарю!

    @nurlanych116@nurlanych1162 ай бұрын
  • спасибо, очень полезно

    @Vitaly-xu3ly@Vitaly-xu3ly2 ай бұрын
  • Отличный стрим. В идеале еще бы ко всему этому навернуть мультиплатформу

    @luci.morningstar@luci.morningstar2 ай бұрын
    • Это уже в отдельной части

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Крутой формат!! Очень полезно. Спасибо По поводу рутины - для недоджунов(для меня) хороший наглядный опыт.

    @user-lu4ws2nh5v@user-lu4ws2nh5vАй бұрын
  • Вот прям все отлично. Спасибо большое за труды. Мне, как начинающему, прям помогает

    @dream_line70@dream_line70Ай бұрын
    • Спасибо 😊

      @AndroidBroadcast@AndroidBroadcastАй бұрын
  • Очень крутая рубрика , очень хотелось бы увидеть продолжение !

    @itshlyevvibes1779@itshlyevvibes17792 ай бұрын
    • Обязательно будет

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Спасибо❤

    @tennazemle@tennazemle2 ай бұрын
  • волшебный комментарий!

    @pavelivan8272@pavelivan82722 ай бұрын
  • Топ стрим! Ждем еще)

    @alexanderef.1524@alexanderef.15242 ай бұрын
    • GetAllArticlesUseCase - Кирилл, не забудь поправить)

      @alexanderef.1524@alexanderef.15242 ай бұрын
  • Кирилл, огромное спасибо за уделённое время, ты топ)

    @stasleonov5196@stasleonov51962 ай бұрын
    • Спасибо ))) Очень приятно читать такое

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Очень круто. Продолжай такой формат!

    @user-fj8df8gb4b@user-fj8df8gb4b2 ай бұрын
    • Спасибо

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Волшебный комментарий.

    @neyasbit@neyasbit2 ай бұрын
  • Супер!

    @rbobroff@rbobroff2 ай бұрын
  • спасибо!

    @user-kk1yi2sz1d@user-kk1yi2sz1d2 ай бұрын
  • супер!

    @atabekkk@atabekkk2 ай бұрын
  • Кирилл, спасибо Вам за контент. Узнал из курса как можно жить без buildSrc и о новых вариантах. Так же понял как действительно нужно организовывать модули. Освежил знания по Room вместе с Вами и узнал о @Embedded. Решил применить данные знания в своём проекте. Жду с нетерпением субботы в надежде увидеть вторую часть так как хочу понять как нужно правильно работать с Flow. Да, у Вас на канале много инфы, и про Flow есть, но хочется увидеть как Вы дружите Flow с Room и Api. Материал простой, той степени пояснений что Вы даёте вполне хватает и это радует. С уважением, Владимир!

    @minds_clear_02.23_@minds_clear_02.23_2 ай бұрын
    • Спасибо

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Привет! Классное приложение. Я тоже недавно сделал с даггером Новостное приложение и выложил на гитхаб. Только у меня не компоуз. Теперь буду сравнивать . Улучшать.

    @user-rg3hc5nd5p@user-rg3hc5nd5p2 ай бұрын
  • Просто огонь!!! Однозначно нужно продолжать именно в таком формате. Зачем нужен юзКейс который фактически ничего не делает (просто ради клина - не аргумент)? Да, и реализация репозитория очень интересна. Хотелось бы увидеть такую работу: мы сразу возварщаем то что есть в локальной базе, потом делаем запрос на api и если там что то есть обновляем локальную базу (и на ui получает обновлённые данные). Но нужно как то пердусмотреть свайпТоРефрешь с UI, что бы пользователь мог принудительно сказать репозиторию, что нужно перезапросить api. Вот этот момент очень интересует, как лучше это реализовать.

    @user-qu6mb9il6w@user-qu6mb9il6w2 ай бұрын
    • Супер, я как раз скоро буду на Boosty идеи собирать

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Стрим🔥🔥🔥🔥 Очень полезный контент!!!Огромная ответственность накладывается на тебя, теперь этот код станет шаблоном всех новичков ) Это то, что ждали очень долго многие разработчики, жду продолжения!

    @viktorkim234@viktorkim2342 ай бұрын
    • Теперь стало страшнее писать что-то в онлайне )))

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Думал, что у тебя уже больше подписантов, подписался и лайк сделал

    @user-ux8ru5jv7y@user-ux8ru5jv7yАй бұрын
    • спасибо

      @AndroidBroadcast@AndroidBroadcastАй бұрын
  • Все круть, продолжай пожвлуйста

    @user-xs7lx3fw9c@user-xs7lx3fw9c2 ай бұрын
  • Соответственно!

    @captain.automatik@captain.automatik2 ай бұрын
    • И есть ещё одна проблема: в 1080p слишком плохо

      @captain.automatik@captain.automatik2 ай бұрын
    • Следующий стрим будет по другому сделан, но выше 1080p делать не нашел как на macOS

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • 🔥🔥🔥

    @ramilskl@ramilskl2 ай бұрын
  • Продолжайте стримы в подобном формате!

    @user-cb4ns3cj1n@user-cb4ns3cj1n2 ай бұрын
  • кинул комментарий и лайк, ХОЧУ ИЩО!))

    @dreamwalker9380@dreamwalker93802 ай бұрын
  • очень круто, прямо много нового узнал, даже подметил для себя моменты, где я бы лучше сделал. Кажется, что можно модели DBO и DTO разнести в отдельный модуль тк в разных местах используется часто

    @rarrar2525@rarrar25252 ай бұрын
    • Так они и есть в разных модулях, но их конвертирование выполняется в одном модуле

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • От вашего слова-паразита "соответственно" у меня аж разболелась голова (и я не шучу, в минуту до 15 раз можно его услышать было)) А так хорошее видео для новичков, спасибо

    @EugeneVoronoy@EugeneVoronoy2 ай бұрын
  • Кайф

    @limrvl9227@limrvl92272 ай бұрын
  • Воу как раз нам такое дз задавали

    @skala6933@skala69332 ай бұрын
  • Просто отлично! Давай дальше! Если покажешь, как подключить hilt через плагин в version catalog, будет здорово. Я как раз создал новй проект, но как плагин хилта добавить - не нашел в документации.

    @androidkotlin-6234@androidkotlin-62342 ай бұрын
    • Все свои пожелания лучше оставлять на Boosty. Ориентируюcь на хотелки платных подписчиков

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Них.я не понятно, но очень интересно ;)

    @hashstalker@hashstalker2 ай бұрын
  • Очень крутой контент , рад что закончили всю серию роликов полностью Правильно ли я понимаю что в итоге получиться хоть и простое, но по качеству кода продакшн реди решение ?

    @user-cs2bs4fh6t@user-cs2bs4fh6t9 күн бұрын
    • не скажу что прямо так, но старался полностью именно так. Вот следующая разработка уже будет являться такой

      @AndroidBroadcast@AndroidBroadcast7 күн бұрын
  • 00:17 Creating a news app from scratch 02:36 Creating an Android project for the news app 06:45 Creating a new project with documentation and methods 08:40 Preparing to publish the app on Google Play 13:16 Using standard stack for API calls and UI design enhancements. 15:08 Using a Java library for easier retrofit and to speed up build time 22:34 Setting up a simple interface and API 27:11 Configuring settings and pagination for an app 32:45 Creating classes and handling article ACL in response. 36:07 Process of creating the news app from scratch. 41:53 Recommend connecting things right away 44:38 Adding links to documentation for easy access 49:57 Understanding the setup of a news application 53:52 Importance of hiding APIs and using non-modifiable classes 58:49 Implementing interceptors to handle HTTP requests 1:01:33 Understanding the use of interceptors in developing a news app 1:07:47 Reusing and folding modules, leaving public fields for models 1:09:37 Choosing SR over other options for news app development 1:15:11 Connecting modules and using dependencies 1:18:19 Explanation on working with the model and features 1:23:05 Implementing caching for search queries 1:24:52 Creating a local cache and assigning IDs for data 1:28:59 Understanding the advantages of dividing into modules in app development 1:30:56 Creating an Android library module for features. 1:36:47 Creating a new main model for the app 1:39:55 Creating an app with different states and contents 1:44:23 Developing basic CRUD methods for data manipulation 1:48:02 Implementing database functionality in the app 1:55:31 Developing custom models and architectural layers for a news app. 1:58:53 Implementation of repository level for the news application 2:04:42 Discussing the process of requesting and retrieving data in app development 2:07:31 Working with Flo to manage requests and database data synchronously Crafted by Merlin AI.

    @android-rg8ty@android-rg8tyАй бұрын
    • Thanks

      @AndroidBroadcast@AndroidBroadcastАй бұрын
  • Полезный коммент

    @veygard@veygard2 ай бұрын
  • Здравствуйте. Подскажите пожалуйста. Хочу начать изучать андроид разработку, но мне интересно создание приложений которые взаимодействуют с внешним железом. Через вай фай или блютуз. Это либо автомобильные устройства, либо что то с умным домом связанное. Например сделать простое приложение, которое будет управлять центральным замком. Ну это чисто пример, потренироваться. Так вот меня интересует вопрос, является ли это каким то стандартом для обычного андроид разработчика или же это особое узкое направление? Просто хочется найти золотую для себя середину, быть ближе к айти и разработке, но при этом ближе к электронике. Но при этом чтобы не получилось что я снова залез в дебри электроники и ушёл от разработки. Может конечно я не совсем понятно изложил мысли) В общем хочется стать востребованным спецом, но при этом не свернуть.

    @alexpetrov521@alexpetrov521Ай бұрын
  • Узнал много нового для себя. Благодарю! Для юзкейсов не нужно делать отдельный модуль? типа домейн?

    @user-jq1rg8zd6f@user-jq1rg8zd6f23 күн бұрын
    • Модули вообще нужно делать если вы видите в этом смысл для себя, хоть с одним классом, хоть на каждый слой

      @AndroidBroadcast@AndroidBroadcast20 күн бұрын
  • Видос оч хороший: много мелких нюансов есть. Общие же есть в кодлабах.

    @denislopatkin6996@denislopatkin6996Ай бұрын
    • Спасибо!

      @AndroidBroadcast@AndroidBroadcast20 күн бұрын
  • Молодец но мне как новичку понка не все понятно. Что за выбор Api Key

    @user-mq9ji9kf7r@user-mq9ji9kf7rАй бұрын
  • Надо было еще подрубить плагин отображающий нажатие горячих клавиш

    @sergey30027@sergey300272 ай бұрын
    • Да, слетел с переездом на другой комп. В следующий раз будут улучшения уже

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • ждем KMM

    @eugenemasyuk2912@eugenemasyuk291218 күн бұрын
  • Кирилл спасибо за труд! единственный что, зачем создавать много модулей и склеить их между собой ? Какой в этом смысл ? не лучше ли обеспечить коммуникации между модулями через "app" модуль чтобы каждый модуль работала самостоятельно не зная о других моделей !?

    @voicetv9048@voicetv90482 ай бұрын
    • Да, так максиммально и будет, но какие модули нужны на уровне API

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Подсветка хоткеев - было бы очень полезно

    @ildar2244@ildar22442 ай бұрын
    • В следующих частях уже есть

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • При добавлении библиотеки (retrofit2-kotlinx-serialization-converter), gradle выдает ошибку: "Could not resolve com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0.".Но, при добавлении этой же библиотеки в android library или android app модули - все хорошо. В чем может быть проблема???

    @sergeikuznetsov5187@sergeikuznetsov51872 ай бұрын
    • Не знаю. Надо разбираться в коде

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
    • @@AndroidBroadcast Точно такая же проблема и с заново созданным проектом. Подтягиваются только версии 0.8.0 или более ранние (android studio Hedgehog).

      @sergeikuznetsov5187@sergeikuznetsov51872 ай бұрын
  • Библиотека от Jake Wharton перешла в состояние DEPRECATED и теперь предлагается использовать функционал в самой либе Retrofit2. Но в версии 2.9.0 ничего такого нет. И почему все через сериализацию и JSON, а не через Moshi?

    @normno@normnoАй бұрын
    • targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8 Чтобы исправить проблему с установкой Retrofit2 2.10.0

      @normno@normnoАй бұрын
  • Фичи можно создавать прямо в папке, достаточно кликнуть на ту папку, где нужно ее создать

    @somebody2083@somebody20832 ай бұрын
    • Я пробовал, но модуль создается всеравно в корне

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
    • @@AndroidBroadcast даже если написать :feature:... ? У меня работает на последней стейбл версии

      @somebody2083@somebody20832 ай бұрын
  • ребят, посоветуйте годный гайд по грейдлу, ничего не понял, когда пакеты подключали, почему мы могли их переименовывать. А в остальном вроде +- понятно. Хотя я изучаю ща Flutter и немного Android. Flutter кажется проще, а вот в Android кажется что слишком много аннотации и всякого прочего, аж глаза разбегаются

    @junovevo4961@junovevo49612 ай бұрын
    • Официальная документация по Gradle всё хорошо показыает

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Комментарий

    @stasstupak4193@stasstupak41932 ай бұрын
  • А почему апи не через di инициализируется?

    @somebody2083@somebody20832 ай бұрын
    • Был сделан метод для создания экземпляра, а вот уже потом этот метод будет вызываться в DI

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • А ссылка на исходники есть?

    @mironoff2007@mironoff2007Ай бұрын
    • Они пока только для платных подписчиков boosty.to/androidbroadcast/posts/2c152ca5-5b5a-4166-9e35-3c3f03cab104 Будут открыты после завершения проекта

      @AndroidBroadcast@AndroidBroadcastАй бұрын
  • Часть 1? А где часть 0?

    @user-kg4ld9ds5e@user-kg4ld9ds5e2 ай бұрын
    • 😂

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • В конце стало больно смотреть на обилие оберток. Может сделать combine(flow = flowOfState, flow1 = articlesDao.getAll()). Тогда на выходе будет единый флоу на который можно подписываться. Логика загрузки статей также вызвала вопросы. К примеру в текущей реализации нет single source of truth. Может грузить с апи в базу а та в свою очередь автоматов во флоу все отдаст?

    @dmitrytimofeev4826@dmitrytimofeev48262 ай бұрын
    • Сделай и покажи.

      @ephitariathegame2brainstud996@ephitariathegame2brainstud9962 ай бұрын
    • @@ephitariathegame2brainstud996 Не хотел кого-то обидеть. Просто пытаюсь участвовать в жизни сообщества по мере сил. Дайте пожалуйста ссылку на результат текущего с текущего стрима. Переделаю и кину

      @dmitrytimofeev4826@dmitrytimofeev48262 ай бұрын
    • Я еще покажу решения и потом обсудим

      @AndroidBroadcast@AndroidBroadcast2 ай бұрын
  • Очень круто. Хочется продолжения

    @yuras784@yuras7842 ай бұрын
KZhead