Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.eatssu.android.data.local

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

private val Context.appFeatureDataStore: DataStore<Preferences> by preferencesDataStore(name = "app_feature")

class AppFeatureDataStore @Inject constructor(
@ApplicationContext private val context: Context
) {

companion object {
private val ANYONE_BUT_ME_EVENT_POPUP_DISMISSED =
booleanPreferencesKey("anyone_but_me_event_popup_dismissed")
}

val isAnyoneButMeEventPopupDismissed: Flow<Boolean> = context.appFeatureDataStore.data
.map { preferences ->
preferences[ANYONE_BUT_ME_EVENT_POPUP_DISMISSED] ?: false
}

suspend fun setAnyoneButMeEventPopupDismissed(dismissed: Boolean) {
context.appFeatureDataStore.edit { preferences ->
preferences[ANYONE_BUT_ME_EVENT_POPUP_DISMISSED] = dismissed
}
}
}
37 changes: 26 additions & 11 deletions app/src/main/java/com/eatssu/android/presentation/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import androidx.work.WorkManager
import com.eatssu.android.R
import com.eatssu.android.databinding.ActivityMainBinding
import com.eatssu.android.presentation.base.BaseActivity
import com.eatssu.android.presentation.event.AnyoneButMeEventPopupController
import com.eatssu.android.presentation.event.AnyoneButMeEventTooltipController
import com.eatssu.android.presentation.login.LoginActivity
import com.eatssu.android.presentation.mypage.MyPageViewModel
import com.eatssu.android.presentation.mypage.terms.WebViewActivity
import com.eatssu.android.presentation.mypage.userinfo.UserInfoActivity
import com.eatssu.android.presentation.util.showInfoToast
import com.eatssu.android.presentation.util.showToast
Expand All @@ -45,6 +46,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>(
@Inject
lateinit var workManager: WorkManager

@Inject
lateinit var anyoneButMeEventPopupController: AnyoneButMeEventPopupController

@Inject
lateinit var anyoneButMeEventTooltipController: AnyoneButMeEventTooltipController

private val mainViewModel: MainViewModel by viewModels()
private val myPageViewModel: MyPageViewModel by viewModels()

Expand All @@ -54,6 +61,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(

setupNoToolbar()
setNavigation()
bindEventPopup(showOnLaunch = savedInstanceState == null)
bindEventTooltip()

checkAlarmPermission()
collectState()
Expand All @@ -79,15 +88,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(
}

R.id.anyone_but_me_menu -> {
startActivity<WebViewActivity> {
putExtra(WebViewActivity.EXTRA_URL, getString(R.string.anyone_but_me_url))
putExtra(WebViewActivity.EXTRA_TITLE, getString(R.string.nav_anyone_but_me))
putExtra("SCREEN_ID", ScreenId.ANYONE_BUT_ME_MAIN.name)
putExtra(
WebViewActivity.EXTRA_BACK_ICON_RES_ID,
com.eatssu.design_system.R.drawable.ic_close
)
}
anyoneButMeEventPopupController.openAnyoneButMePage()
false
}

Expand All @@ -103,6 +104,21 @@ class MainActivity : BaseActivity<ActivityMainBinding>(
}
}

private fun bindEventPopup(showOnLaunch: Boolean) {
anyoneButMeEventPopupController.bind(
composeView = binding.composeEventPopup,
lifecycleScope = lifecycleScope,
showOnLaunch = showOnLaunch
)
}

private fun bindEventTooltip() {
anyoneButMeEventTooltipController.bind(
tooltipComposeView = binding.composeEventTooltip,
bottomNavigationView = binding.bottomNaviBar
)
}

// set UI --
private fun setupNoToolbar() {
// 툴바 사용하지 않도록 설정
Expand Down Expand Up @@ -211,6 +227,5 @@ class MainActivity : BaseActivity<ActivityMainBinding>(
}
}


override fun shouldLogScreenId() = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package com.eatssu.android.presentation.event

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.eatssu.android.R
import com.eatssu.design_system.theme.EatssuTheme
import com.eatssu.design_system.theme.White

private val EventButton = Color(0xFF1F1F1F)

@Composable
fun AnyoneButMeEventDialog(
onDismiss: () -> Unit,
onDismissForever: () -> Unit,
onInstagramClick: () -> Unit,
onAnyoneButMeClick: () -> Unit,
) {
Dialog(
onDismissRequest = onDismiss,
properties = DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = false,
usePlatformDefaultWidth = false
)
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 20.dp),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.spacedBy(11.dp)
) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onAnyoneButMeClick),
shape = RoundedCornerShape(20.dp),
colors = CardDefaults.cardColors(containerColor = White)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(640f / 720f),
) {
Image(
painter = painterResource(R.drawable.ic_event_popup),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Fit
)

Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 40.dp)
) {
InstagramButton(onClick = onInstagramClick)
}
}
}

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
FooterActionText(
text = "다시 보지 않기",
onClick = onDismissForever
)
FooterActionText(
text = "닫기",
onClick = onDismiss
)
}
}
}
}
}

@Composable
private fun InstagramButton(onClick: () -> Unit) {
Row(
modifier = Modifier
.clip(RoundedCornerShape(999.dp))
.background(EventButton)
.border(1.dp, White, RoundedCornerShape(999.dp))
.clickable(onClick = onClick)
.padding(start = 16.dp, end = 8.dp, top = 8.dp, bottom = 8.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "EAT-SSU 인스타그램 바로가기",
color = White,
style = EatssuTheme.typography.body2
)
Box(
modifier = Modifier
.size(24.dp)
.background(White, CircleShape),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(R.drawable.ic_arrow_right),
contentDescription = null,
modifier = Modifier.size(12.dp)
)
}
}
}

@Composable
private fun FooterActionText(
text: String,
onClick: () -> Unit,
) {
Text(
text = text,
modifier = Modifier.clickable(onClick = onClick),
color = White,
style = EatssuTheme.typography.body2
)
}

@Preview(showBackground = true)
@Composable
private fun AnyoneButMeEventDialogPreview() {
EatssuTheme {
Box(
modifier = Modifier
.fillMaxWidth()
.height(520.dp)
) {
AnyoneButMeEventDialog(
onDismiss = {},
onDismissForever = {},
onInstagramClick = {},
onAnyoneButMeClick = {}
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.eatssu.android.presentation.event

import android.content.Context
import android.content.Intent
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.LifecycleCoroutineScope
import com.eatssu.android.R
import com.eatssu.android.data.local.AppFeatureDataStore
import com.eatssu.android.presentation.mypage.terms.WebViewActivity
import com.eatssu.android.presentation.util.openInBrowser
import com.eatssu.common.enums.ScreenId
import com.eatssu.design_system.theme.EatssuTheme
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject

@ActivityScoped
class AnyoneButMeEventPopupController @Inject constructor(
@ActivityContext private val context: Context,
private val appFeatureDataStore: AppFeatureDataStore,
) {
private lateinit var composeView: ComposeView
private lateinit var lifecycleScope: LifecycleCoroutineScope
private var canAutoShowOnLaunch = false
private var hasHandledLaunchPopup = false
private val isPopupVisible = mutableStateOf(false)

fun bind(
composeView: ComposeView,
lifecycleScope: LifecycleCoroutineScope,
showOnLaunch: Boolean,
) {
this.composeView = composeView
this.lifecycleScope = lifecycleScope
canAutoShowOnLaunch = showOnLaunch
setupContent()
observePopupState()
}

private fun setupContent() {
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
composeView.setContent {
EatssuTheme {
if (isPopupVisible.value) {
AnyoneButMeEventDialog(
onDismiss = ::hide,
onDismissForever = ::dismissForever,
onInstagramClick = ::openInstagram,
onAnyoneButMeClick = ::openAnyoneButMePage
)
}
}
}
}

private fun observePopupState() {
lifecycleScope.launch {
appFeatureDataStore.isAnyoneButMeEventPopupDismissed.collectLatest { dismissed ->
if (canAutoShowOnLaunch && !hasHandledLaunchPopup && !dismissed) {
hasHandledLaunchPopup = true
isPopupVisible.value = true
}
}
}
}

private fun dismissForever() {
hide()
lifecycleScope.launch {
appFeatureDataStore.setAnyoneButMeEventPopupDismissed(true)
}
}

fun openAnyoneButMePage() {
hide()
context.startActivity(
Intent(context, WebViewActivity::class.java).apply {
putExtra(WebViewActivity.EXTRA_URL, context.getString(R.string.anyone_but_me_url))
putExtra(WebViewActivity.EXTRA_TITLE, context.getString(R.string.nav_anyone_but_me))
putExtra("SCREEN_ID", ScreenId.ANYONE_BUT_ME_MAIN.name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

인텐트 extra에 사용되는 키 "SCREEN_ID"가 하드코딩되어 있습니다. WebViewActivity의 companion object에 상수로 정의하고, 이를 참조하여 사용하는 것이 타입 안정성을 높이고 실수를 방지하는 데 도움이 됩니다. 예를 들어, WebViewActivity.EXTRA_SCREEN_ID와 같이 정의할 수 있습니다.

putExtra(
WebViewActivity.EXTRA_BACK_ICON_RES_ID,
com.eatssu.design_system.R.drawable.ic_close
)
}
)
}

private fun openInstagram() {
hide()
context.openInBrowser(context.getString(R.string.eatssu_event_instagram_url))
}

private fun hide() {
canAutoShowOnLaunch = false
isPopupVisible.value = false
}
}
Loading
Loading