Commit 6bc7f456 authored by Vladislav Bogdashkin's avatar Vladislav Bogdashkin 🎣

Merge branch 'develop' of gitlab.biganto.com:bigantoBogdashkin/room-park-android into develop

parents 78caf41a 51122052
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
......
...@@ -17,7 +17,7 @@ android { ...@@ -17,7 +17,7 @@ android {
defaultConfig { defaultConfig {
applicationId $APPLICATION_ID applicationId $APPLICATION_ID
ndk { ndk {
abiFilters 'armeabi-v7a', 'x86' abiFilters 'armeabi-v7a', 'arm64-v8a'
} }
minSdkVersion minSdkVersion_RoomPark minSdkVersion minSdkVersion_RoomPark
targetSdkVersion targetSdkVersion_RoomPark targetSdkVersion targetSdkVersion_RoomPark
......
...@@ -7,7 +7,7 @@ import com.biganto.visual.roompark.data.repository.mapper.fromRaw ...@@ -7,7 +7,7 @@ import com.biganto.visual.roompark.data.repository.mapper.fromRaw
import com.biganto.visual.roompark.data.repository.mapper.fromRawList import com.biganto.visual.roompark.data.repository.mapper.fromRawList
import com.biganto.visual.roompark.domain.contract.FeedsContract import com.biganto.visual.roompark.domain.contract.FeedsContract
import com.biganto.visual.roompark.domain.custom_exception.CustomApiException import com.biganto.visual.roompark.domain.custom_exception.CustomApiException
import com.biganto.visual.roompark.domain.model.ArticlePreviewModel import com.biganto.visual.roompark.domain.model.ArticleModel
import com.biganto.visual.roompark.domain.model.ArticlesPreviewModel import com.biganto.visual.roompark.domain.model.ArticlesPreviewModel
import com.biganto.visual.roompark.domain.model.FeedModel import com.biganto.visual.roompark.domain.model.FeedModel
import com.biganto.visual.roompark.domain.model.fromEntity import com.biganto.visual.roompark.domain.model.fromEntity
...@@ -37,9 +37,8 @@ class FeedsContractModule @Inject constructor( ...@@ -37,9 +37,8 @@ class FeedsContractModule @Inject constructor(
override fun fetchFeedObservable(id: Int,pageSize:Int, startIndex:Int) override fun fetchFeedObservable(id: Int,pageSize:Int, startIndex:Int)
: Observable<ArticlesPreviewModel> = fetchArticles(id,pageSize,startIndex) : Observable<ArticlesPreviewModel> = fetchArticles(id,pageSize,startIndex)
override fun getArticle(id: Int): Observable<ArticlePreviewModel> { override fun getArticle(id: Int): Observable<ArticleModel> =
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. fetchArticle(id)
}
init { init {
Timber.d("Feeds Repository Created") Timber.d("Feeds Repository Created")
...@@ -104,5 +103,37 @@ class FeedsContractModule @Inject constructor( ...@@ -104,5 +103,37 @@ class FeedsContractModule @Inject constructor(
arrayListOf(fetchFeedsApi,fetchFeedsDb) arrayListOf(fetchFeedsApi,fetchFeedsDb)
).map { fromEntity(it,::fromEntity) } ).map { fromEntity(it,::fromEntity) }
private fun fetchArticleApi(id:Int) =
api.getArticle(id)
.doOnNext { Timber.d("raw0 $it") }
.flatMap {article ->
db.getFeed(article.feed_alias).observable()
.map { fromRaw(article,it.id) }
}
.flatMap { article ->
db.getArticle(article.id).observable()
.map { articleEntity ->
articleEntity.setBody(article.body)
articleEntity.setPublished(article.published)
articleEntity.photo = article.photo
articleEntity.setTitle(article.title)
articleEntity
}
}
.map { arrayListOf(it)}
.doOnNext(db::blockingUpsert)
.map { it.first() }
.subscribeOn(Schedulers.io())
private fun fetchArticleDb(id:Int) =
db.getArticle(id).observable()
.filter{it.body != null}
.subscribeOn(Schedulers.io())
private fun fetchArticle(articleId:Int): Observable<ArticleModel> =
Observable.mergeDelayError(
arrayListOf(fetchArticleApi(articleId),fetchArticleDb(articleId))
).map { fromEntity(it,true) }
} }
...@@ -22,4 +22,6 @@ interface IDb { ...@@ -22,4 +22,6 @@ interface IDb {
fun getTopLevelAlbums(): Observable<ImageAlbumEntity> fun getTopLevelAlbums(): Observable<ImageAlbumEntity>
fun getChildAlbums(parentId: Int): Observable<ImageAlbumEntity> fun getChildAlbums(parentId: Int): Observable<ImageAlbumEntity>
fun checkIfExistsAlbumJunction(albumId: Int, parentAlbumId: Int): ImageAlbumJunctionEntity? fun checkIfExistsAlbumJunction(albumId: Int, parentAlbumId: Int): ImageAlbumJunctionEntity?
fun getFeed(feedAlias: String): ReactiveResult<FeedEntity>
fun getArticle(id: Int): ReactiveResult<ArticleEntity>
} }
\ No newline at end of file
...@@ -67,6 +67,14 @@ class RequeryRepository @Inject constructor( ...@@ -67,6 +67,14 @@ class RequeryRepository @Inject constructor(
fetchAll<FeedEntity>().where(FeedEntity.ID.eq(feedId)) fetchAll<FeedEntity>().where(FeedEntity.ID.eq(feedId))
.get() .get()
override fun getFeed(feedAlias:String): ReactiveResult<FeedEntity> =
fetchAll<FeedEntity>().where(FeedEntity.ALIAS.eq(feedAlias))
.get()
override fun getArticle(id:Int): ReactiveResult<ArticleEntity> =
fetchAll<ArticleEntity>().where(ArticleEntity.ID.eq(id))
.get()
override fun getTopLevelAlbums(): Observable<ImageAlbumEntity> = override fun getTopLevelAlbums(): Observable<ImageAlbumEntity> =
fetchAll<ImageAlbumEntity>() fetchAll<ImageAlbumEntity>()
.get().observable() .get().observable()
......
...@@ -26,5 +26,5 @@ interface Article : Persistable { ...@@ -26,5 +26,5 @@ interface Article : Persistable {
@get:ManyToOne(cascade = [CascadeAction.NONE]) @get:ManyToOne(cascade = [CascadeAction.NONE])
val feed : Feed? val feed : Feed?
@get:Convert(TitledPhotoListConverter::class) @get:Convert(TitledPhotoListConverter::class)
val photo : List<TitledPhoto>? var photo : List<TitledPhoto>?
} }
\ No newline at end of file
...@@ -2,6 +2,7 @@ package com.biganto.visual.roompark.data.repository.mapper ...@@ -2,6 +2,7 @@ package com.biganto.visual.roompark.data.repository.mapper
import android.content.res.Resources import android.content.res.Resources
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.* import com.biganto.visual.roompark.data.repository.api.retrofit.raw.*
import com.biganto.visual.roompark.data.repository.db.requrey.TitledPhoto
import com.biganto.visual.roompark.data.repository.db.requrey.model.ArticleEntity import com.biganto.visual.roompark.data.repository.db.requrey.model.ArticleEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.FeedEntity import com.biganto.visual.roompark.data.repository.db.requrey.model.FeedEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.ImageAlbumEntity import com.biganto.visual.roompark.data.repository.db.requrey.model.ImageAlbumEntity
...@@ -61,6 +62,29 @@ fun fromRaw(raw:NewsArticleRaw,feedId:Int):ArticleEntity{ ...@@ -61,6 +62,29 @@ fun fromRaw(raw:NewsArticleRaw,feedId:Int):ArticleEntity{
} }
fun fromRaw(raw:ArticleRaw,feedId: Int):ArticleEntity{
val entity = ArticleEntity()
entity.setId(raw.id)
entity.setPublished(raw.published)
entity.setTitle(raw.title)
entity.setBody(raw.body)
raw.photo?.let {
entity.photo = fromRawList(it,::fromRaw)
}
val feed = FeedEntity()
feed.setId(feedId)
// feed.setAlias(raw.feed_alias)
entity.setFeed(feed)
return entity
}
fun fromRaw(raw:NewsPhotoRaw):TitledPhoto{
return TitledPhoto(raw.title,raw.url)
}
fun fromRaw(raw:ImageAlbumRaw) : ImageAlbumEntity { fun fromRaw(raw:ImageAlbumRaw) : ImageAlbumEntity {
val entity = ImageAlbumEntity() val entity = ImageAlbumEntity()
entity.setId(raw.id) entity.setId(raw.id)
......
package com.biganto.visual.roompark.domain.contract package com.biganto.visual.roompark.domain.contract
import com.biganto.visual.roompark.domain.model.ArticlePreviewModel import com.biganto.visual.roompark.domain.model.ArticleModel
import com.biganto.visual.roompark.domain.model.ArticlesPreviewModel import com.biganto.visual.roompark.domain.model.ArticlesPreviewModel
import com.biganto.visual.roompark.domain.model.FeedModel import com.biganto.visual.roompark.domain.model.FeedModel
import io.reactivex.Observable import io.reactivex.Observable
...@@ -12,7 +12,7 @@ import io.reactivex.Observable ...@@ -12,7 +12,7 @@ import io.reactivex.Observable
interface FeedsContract{ interface FeedsContract{
fun fetchFeeds(): Observable<List<FeedModel>> fun fetchFeeds(): Observable<List<FeedModel>>
fun getArticle(id:Int): Observable<ArticlePreviewModel> fun getArticle(id:Int): Observable<ArticleModel>
fun fetchFeedObservable(id: Int): Observable<ArticlesPreviewModel> fun fetchFeedObservable(id: Int): Observable<ArticlesPreviewModel>
fun fetchFeedObservable( fun fetchFeedObservable(
id: Int, id: Int,
......
package com.biganto.visual.roompark.domain.interactor package com.biganto.visual.roompark.domain.interactor
import com.biganto.visual.roompark.domain.model.ArticleModel import com.biganto.visual.roompark.domain.model.ArticleModel
import io.reactivex.Single import com.biganto.visual.roompark.domain.use_case.FeedUseCase
import io.reactivex.Observable
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
...@@ -10,14 +11,17 @@ import javax.inject.Inject ...@@ -10,14 +11,17 @@ import javax.inject.Inject
*/ */
class ArticleInteractor @Inject constructor( class ArticleInteractor @Inject constructor(
private val uc:FeedUseCase
) { ) {
fun fetchArticles(articleId: Int): Single<ArticleModel> = Single.just( fun fetchArticle(articleId: Int): Observable<ArticleModel> =
when (articleId) { uc.getArticle(articleId)
2 -> article2 // Single.just(
else -> error("unknown feedId") // when (articleId) {
} // 2 -> article2
) // else -> error("unknown feedId")
// }
// )
companion object{ companion object{
...@@ -31,6 +35,7 @@ class ArticleInteractor @Inject constructor( ...@@ -31,6 +35,7 @@ class ArticleInteractor @Inject constructor(
"\n" + "\n" +
"Более подробная информация доступна в офисе продаж по телефону: +7 (495) 127-86-86", "Более подробная информация доступна в офисе продаж по телефону: +7 (495) 127-86-86",
"https://room-park.ru/assets/news_articles/preview/00/00/00/96-a972cd.jpeg", "https://room-park.ru/assets/news_articles/preview/00/00/00/96-a972cd.jpeg",
null,
false false
) )
} }
......
package com.biganto.visual.roompark.domain.model package com.biganto.visual.roompark.domain.model
import com.biganto.visual.roompark.data.repository.db.requrey.TitledPhoto
import com.biganto.visual.roompark.data.repository.db.requrey.model.ArticleEntity import com.biganto.visual.roompark.data.repository.db.requrey.model.ArticleEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.FeedEntity import com.biganto.visual.roompark.data.repository.db.requrey.model.FeedEntity
import java.util.* import java.util.*
...@@ -8,6 +9,7 @@ import java.util.* ...@@ -8,6 +9,7 @@ import java.util.*
* Created by Vladislav Bogdashkin on 23.09.2019. * Created by Vladislav Bogdashkin on 23.09.2019.
*/ */
const val DEFAULT_ROOM_PARK_PREVIEW = "https://room-park.ru/assets/news_articles/preview/00/00/00/15-fe1886.jpeg"
data class FeedModel(val feedId:Int, val title:String, val alias:String? = null) data class FeedModel(val feedId:Int, val title:String, val alias:String? = null)
...@@ -37,6 +39,7 @@ data class ArticleModel( ...@@ -37,6 +39,7 @@ data class ArticleModel(
val title:String, val title:String,
val htmlBody:String, val htmlBody:String,
val previewUrl:String, val previewUrl:String,
val bottomPhotoList:List<TitledPhoto>?,
val isRead:Boolean val isRead:Boolean
) )
...@@ -61,4 +64,15 @@ fun fromEntity(parentId:Int,entity: List<ArticleEntity>):ArticlesPreviewModel = ...@@ -61,4 +64,15 @@ fun fromEntity(parentId:Int,entity: List<ArticleEntity>):ArticlesPreviewModel =
articles = fromEntity(entity,::fromEntity) articles = fromEntity(entity,::fromEntity)
) )
fun <E,M> fromEntity(raw: List<E>,block:(E)->M):List<M> = List(raw.size) { index-> block(raw[index]) } fun fromEntity(entity: ArticleEntity,read:Boolean) : ArticleModel =
ArticleModel(
articleId = entity.id,
published = entity.published,
title = entity.title,
htmlBody = entity.body!!,
previewUrl = entity.preview?:DEFAULT_ROOM_PARK_PREVIEW,
bottomPhotoList = entity.photo,
isRead = read
)
fun <E,M> fromEntity(raw: List<E>,block:(E)->M):List<M> = List(raw.size) { index-> block(raw[index])}
...@@ -10,7 +10,6 @@ import javax.inject.Inject ...@@ -10,7 +10,6 @@ import javax.inject.Inject
class FeedUseCase @Inject constructor( class FeedUseCase @Inject constructor(
private val contract: FeedsContract private val contract: FeedsContract
){ ){
fun getFeeds() = contract.fetchFeeds() fun getFeeds() = contract.fetchFeeds()
fun getArticles(feedId:Int) = fun getArticles(feedId:Int) =
...@@ -20,5 +19,6 @@ class FeedUseCase @Inject constructor( ...@@ -20,5 +19,6 @@ class FeedUseCase @Inject constructor(
contract.fetchFeedObservable(feedId,pageSize,startIndex) contract.fetchFeedObservable(feedId,pageSize,startIndex)
fun getArticle(articleId:Int) =
contract.getArticle(articleId)
} }
\ No newline at end of file
...@@ -6,6 +6,7 @@ import android.view.ViewGroup ...@@ -6,6 +6,7 @@ import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick
import com.biganto.visual.roompark.R import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.RoomParkApplication import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity import com.biganto.visual.roompark.base.RoomParkMainActivity
...@@ -50,6 +51,10 @@ class ArticleScreenController : ...@@ -50,6 +51,10 @@ class ArticleScreenController :
@BindView(R.id.articleContent) @BindView(R.id.articleContent)
lateinit var contentView: MaterialTextView lateinit var contentView: MaterialTextView
@BindView(R.id.articleCloseButton)
lateinit var closeArticle: FloatingActionButton
val blurPreview:ImageView by lazy { val blurPreview:ImageView by lazy {
headerBlock.findViewById<ImageView>(R.id.articlePreviewBlurred) headerBlock.findViewById<ImageView>(R.id.articlePreviewBlurred)
} }
...@@ -66,6 +71,11 @@ class ArticleScreenController : ...@@ -66,6 +71,11 @@ class ArticleScreenController :
headerBlock.findViewById<FloatingActionButton>(R.id.articleCloseButton) headerBlock.findViewById<FloatingActionButton>(R.id.articleCloseButton)
} }
@OnClick(R.id.articleCloseButton)
fun onCloseArticle(){
handleBack()
}
private fun setToolbar(){ private fun setToolbar(){
...@@ -118,7 +128,9 @@ class ArticleScreenController : ...@@ -118,7 +128,9 @@ class ArticleScreenController :
} }
private fun getComponent() = DaggerArticleScreenComponent.factory() private fun getComponent() = DaggerArticleScreenComponent.factory()
.create(RoomParkApplication.component,activity as RoomParkMainActivity) .create(RoomParkApplication.component
,activity as RoomParkMainActivity
,args.getInt(ARTICLE_ID))
.inject(this) .inject(this)
override fun getLayoutId(): Int = R.layout.feed_read_screen override fun getLayoutId(): Int = R.layout.feed_read_screen
......
...@@ -2,14 +2,18 @@ package com.biganto.visual.roompark.presentation.screen.article ...@@ -2,14 +2,18 @@ package com.biganto.visual.roompark.presentation.screen.article
import android.content.Context import android.content.Context
import com.biganto.visual.roompark.base.RoomParkMainActivity import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.data.data_provider.FeedsContractModule
import com.biganto.visual.roompark.di.dagger.AppComponent import com.biganto.visual.roompark.di.dagger.AppComponent
import com.biganto.visual.roompark.di.dagger.PerScreen import com.biganto.visual.roompark.di.dagger.PerScreen
import com.biganto.visual.roompark.domain.contract.FeedsContract
import dagger.Binds import dagger.Binds
import dagger.BindsInstance import dagger.BindsInstance
import dagger.Component import dagger.Component
import dagger.Module import dagger.Module
import javax.inject.Named
const val ARTICLE_SCREEN_ARTICLEID="ARTICLE_SCREEN_ARTICLEID_KEY"
@PerScreen @PerScreen
@Component( @Component(
modules = [ArticleScreenModule::class], modules = [ArticleScreenModule::class],
...@@ -21,6 +25,7 @@ interface ArticleScreenComponent { ...@@ -21,6 +25,7 @@ interface ArticleScreenComponent {
fun create( fun create(
appComponent: AppComponent appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity ,@BindsInstance activity: RoomParkMainActivity
,@BindsInstance @Named(ARTICLE_SCREEN_ARTICLEID) articleId:Int
): ArticleScreenComponent ): ArticleScreenComponent
} }
...@@ -36,4 +41,7 @@ abstract class ArticleScreenModule{ ...@@ -36,4 +41,7 @@ abstract class ArticleScreenModule{
@Binds @Binds
abstract fun provideContext(activity: RoomParkMainActivity): Context abstract fun provideContext(activity: RoomParkMainActivity): Context
@PerScreen
@Binds
abstract fun provideContract(impl: FeedsContractModule): FeedsContract
} }
...@@ -7,6 +7,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers ...@@ -7,6 +7,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named
/** /**
* Created by Vladislav Bogdashkin on 30.09.2019. * Created by Vladislav Bogdashkin on 30.09.2019.
...@@ -14,16 +15,17 @@ import javax.inject.Inject ...@@ -14,16 +15,17 @@ import javax.inject.Inject
class ArticleScreenPresenter @Inject constructor( class ArticleScreenPresenter @Inject constructor(
private val interactor: ArticleInteractor private val interactor: ArticleInteractor,
@Named(ARTICLE_SCREEN_ARTICLEID) private val selectedArticleId:Int
) )
: BigantoBasePresenter<ArticleScreen, ArticleScreenViewState>() { : BigantoBasePresenter<ArticleScreen, ArticleScreenViewState>() {
override fun defaultErrorViewStateHandler()= override fun defaultErrorViewStateHandler() =
{e:ExceptionString -> ArticleScreenViewState.SomeError(e)} {e:ExceptionString -> ArticleScreenViewState.SomeError(e)}
override fun bindIntents() { override fun bindIntents() {
val prefetchCards = interactor.fetchArticles(2) val prefetchCards = interactor.fetchArticle(selectedArticleId)
.map { ArticleScreenViewState.ArticleLoaded(it) } .map { ArticleScreenViewState.ArticleLoaded(it) }
val state = restoreStateObservable val state = restoreStateObservable
......
...@@ -14,6 +14,7 @@ import com.biganto.visual.roompark.domain.model.AlbumPreviewModel ...@@ -14,6 +14,7 @@ import com.biganto.visual.roompark.domain.model.AlbumPreviewModel
import com.biganto.visual.roompark.domain.model.ArticlePreviewModel import com.biganto.visual.roompark.domain.model.ArticlePreviewModel
import com.biganto.visual.roompark.domain.model.FeedModel import com.biganto.visual.roompark.domain.model.FeedModel
import com.biganto.visual.roompark.domain.model.WebCamModel import com.biganto.visual.roompark.domain.model.WebCamModel
import com.biganto.visual.roompark.presentation.screen.article.ArticleScreenController
import com.biganto.visual.roompark.presentation.screen.feed_list.ArticlesScreenController import com.biganto.visual.roompark.presentation.screen.feed_list.ArticlesScreenController
import com.biganto.visual.roompark.presentation.screen.feeds.utils.AlbumsPreviewAdapter import com.biganto.visual.roompark.presentation.screen.feeds.utils.AlbumsPreviewAdapter
import com.biganto.visual.roompark.presentation.screen.feeds.utils.ArticlesPreviewAdapter import com.biganto.visual.roompark.presentation.screen.feeds.utils.ArticlesPreviewAdapter
...@@ -185,7 +186,11 @@ class FeedsScreenController : ...@@ -185,7 +186,11 @@ class FeedsScreenController :
} }
private fun render(viewState: FeedsScreenViewState.ToArticle) { private fun render(viewState: FeedsScreenViewState.ToArticle) {
TODO("to article screen") router.pushController(
RouterTransaction.with(ArticleScreenController(viewState.articleId))
.popChangeHandler(FadeChangeHandler())
.pushChangeHandler(FadeChangeHandler())
)
} }
private fun render(viewState: FeedsScreenViewState.ToAlbum) { private fun render(viewState: FeedsScreenViewState.ToAlbum) {
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout <LinearLayout
...@@ -12,7 +13,8 @@ ...@@ -12,7 +13,8 @@
android:id="@+id/articleHeaderBlock" android:id="@+id/articleHeaderBlock"
layout="@layout/feed_read_header" layout="@layout/feed_read_header"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:visibility="visible" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/articleTitle" android:id="@+id/articleTitle"
...@@ -34,6 +36,7 @@ ...@@ -34,6 +36,7 @@
Минимальная процентная ставка действительна при покупке любого типа квартир в жилом комплексе – от студий 23,8 кв. м до четырехкомнатных квартир площадью 102,7 кв. м. Минимальный первоначальный взнос составляет от 20%. Минимальная процентная ставка действительна при покупке любого типа квартир в жилом комплексе – от студий 23,8 кв. м до четырехкомнатных квартир площадью 102,7 кв. м. Минимальный первоначальный взнос составляет от 20%.
Более подробная информация доступна в офисе продаж по телефону: +7 (495) 127-86-86" /> Более подробная информация доступна в офисе продаж по телефону: +7 (495) 127-86-86" />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment