Skip to content

Commit

Permalink
빈자리 알림 (#183)
Browse files Browse the repository at this point in the history
Co-authored-by: 양주현 <didwngus01@snu.ac.kr>
  • Loading branch information
eastshine2741 and JuTaK97 authored Aug 9, 2023
1 parent 47e5a44 commit 967ff99
Show file tree
Hide file tree
Showing 110 changed files with 1,598 additions and 155 deletions.
17 changes: 17 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 52 additions & 0 deletions app/src/main/java/com/wafflestudio/snutt2/RemoteConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.wafflestudio.snutt2

import com.wafflestudio.snutt2.data.user.UserRepository
import com.wafflestudio.snutt2.lib.network.ApiOnError
import com.wafflestudio.snutt2.lib.network.SNUTTRestApi
import com.wafflestudio.snutt2.lib.network.dto.core.RemoteConfigDto
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class RemoteConfig @Inject constructor(
api: SNUTTRestApi,
userRepository: UserRepository,
apiOnError: ApiOnError,
) {
private val config = callbackFlow {
userRepository.accessToken.filter { it.isNotEmpty() }.collect {
withContext(Dispatchers.IO) {
try {
send(api._getRemoteConfig())
} catch (e: Exception) {
apiOnError(e)
}
}
}
awaitClose {}
}.stateIn(
CoroutineScope(Dispatchers.Main),
SharingStarted.Eagerly,
RemoteConfigDto()
)

val friendBundleSrc: String
get() = config.value.reactNativeBundleSrc?.src?.get("android") ?: ""

val vacancyNotificationBannerEnabled: Boolean
get() = config.value.vacancyBannerConfig.visible

val sugangSNUUrl: String
get() = config.value.vacancyUrlConfig.url ?: ""

val settingPageNewBadgeTitles: List<String>
get() = config.value.settingsBadgeConfig.new
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fun Modifier.clicks(
throttleMs: Long = 200L,
enabled: Boolean = true,
role: Role? = null,
onClick: () -> Unit
onClick: () -> Unit,
) = composed {
val clickFn = applyEventThrottling(onClick, throttleMs = throttleMs)
clickable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ fun ExitIcon(
@Composable
fun TagIcon(
modifier: Modifier = Modifier,
colorFilter: ColorFilter? = null,
colorFilter: ColorFilter? = ColorFilter.tint(SNUTTColors.Black900),
) {
Image(
modifier = modifier,
Expand Down Expand Up @@ -335,6 +335,19 @@ fun PeopleIcon(
)
}

@Composable
fun ThickReviewIcon(
modifier: Modifier = Modifier,
colorFilter: ColorFilter? = null,
) {
Image(
modifier = modifier,
painter = painterResource(R.drawable.ic_review_thick),
contentDescription = "",
colorFilter = colorFilter,
)
}

@Composable
fun SettingIcon(
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -594,3 +607,69 @@ fun PersonIcon(
colorFilter = colorFilter,
)
}

@Composable
fun DetailIcon(
modifier: Modifier = Modifier,
colorFilter: ColorFilter? = null
) {
Image(
modifier = modifier,
painter = painterResource(id = R.drawable.ic_detail),
contentDescription = "",
colorFilter = colorFilter
)
}

@Composable
fun RingingAlarmIcon(
modifier: Modifier = Modifier,
colorFilter: ColorFilter? = null,
marked: Boolean = false
) {
Image(
modifier = modifier,
painter = painterResource(id = if (marked) R.drawable.ic_ringing_alarm_selected else R.drawable.ic_ringing_alarm_unselected),
contentDescription = "",
colorFilter = colorFilter
)
}

@Composable
fun AddCircleIcon(
modifier: Modifier = Modifier,
colorFilter: ColorFilter? = null
) {
Image(
modifier = modifier,
painter = painterResource(id = R.drawable.ic_add_circle),
contentDescription = "",
colorFilter = colorFilter
)
}

@Composable
fun RemoveCircleIcon(
modifier: Modifier = Modifier,
colorFilter: ColorFilter? = null
) {
Image(
modifier = modifier,
painter = painterResource(id = R.drawable.ic_remove_circle),
contentDescription = "",
colorFilter = colorFilter
)
}

@Composable
fun QuestionCircleIcon(
modifier: Modifier = Modifier,
colorFilter: ColorFilter? = null
) {
Image(
modifier = modifier,
painter = painterResource(id = R.drawable.ic_question_circle),
contentDescription = "",
colorFilter = colorFilter
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ fun CustomDialog(
onDismissRequest = onDismiss,
properties = DialogProperties(usePlatformDefaultWidth = false)
) {
Surface(shape = RoundedCornerShape(10)) {
Surface(
shape = RoundedCornerShape(10),
elevation = 10.dp
) {
Column(modifier = Modifier.width(width ?: (screenWidthInDp - 50.dp)).background(SNUTTColors.White900)) {
title?.let {
Row {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.wafflestudio.snutt2.components.compose

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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.unit.dp
import com.wafflestudio.snutt2.ui.SNUTTColors

@Composable
fun RoundCheckbox(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
checkedBgColor: Color = SNUTTColors.Black900,
uncheckedBgColor: Color = SNUTTColors.Transparent,
checkMarkColor: Color = SNUTTColors.White900,
borderColor: Color = SNUTTColors.VacancyGray
) {
val animatedBgColor: Color by animateColorAsState(if (checked) checkedBgColor else uncheckedBgColor)
val animatedBorderColor: Color by animateColorAsState(if (checked) checkedBgColor else borderColor)
Box(
modifier = modifier
.clip(CircleShape)
.size(20.dp)
.border(width = 2.dp, color = animatedBorderColor, shape = CircleShape)
.background(animatedBgColor)
.clicks {
onCheckedChange?.invoke(checked.not())
},
contentAlignment = Alignment.Center
) {
AnimatedVisibility(
visible = checked,
enter = fadeIn(),
exit = fadeOut()
) {
Icon(
modifier = Modifier.padding(4.dp),
imageVector = Icons.Default.Check,
tint = checkMarkColor,
contentDescription = null
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fun TopBar(
shape = RectangleShape,
color = SNUTTColors.White900,
elevation = 1.dp,
modifier = Modifier
modifier = modifier
.padding(bottom = 2.dp)
.fillMaxWidth()
.height(56.dp)
Expand All @@ -74,7 +74,7 @@ fun TopBar(
}
) {
Row(
modifier = modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Row(
Expand Down
25 changes: 21 additions & 4 deletions app/src/main/java/com/wafflestudio/snutt2/data/SNUTTStorage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package com.wafflestudio.snutt2.data

import com.wafflestudio.snutt2.lib.Optional
import com.wafflestudio.snutt2.lib.network.NetworkLog
import com.wafflestudio.snutt2.lib.network.dto.core.CourseBookDto
import com.wafflestudio.snutt2.lib.network.dto.core.SimpleTableDto
import com.wafflestudio.snutt2.lib.network.dto.core.TableDto
import com.wafflestudio.snutt2.lib.network.dto.core.UserDto
import com.wafflestudio.snutt2.lib.network.dto.core.*
import com.wafflestudio.snutt2.lib.preferences.context.*
import com.wafflestudio.snutt2.model.TableTrimParam
import com.wafflestudio.snutt2.model.TagDto
Expand Down Expand Up @@ -151,6 +148,26 @@ class SNUTTStorage @Inject constructor(
)
)

val firstVacancyVisit = PrefValue<Boolean>(
prefContext,
PrefValueMetaData(
domain = DOMAIN_SCOPE_LOGIN,
key = "first_vacancy_visit",
type = Boolean::class.java,
defaultValue = true
)
)

val vacancyBannerOpenTime = PrefValue<Long>(
prefContext,
PrefValueMetaData(
domain = DOMAIN_SCOPE_LOGIN,
key = "vacancy_banner_open_time",
type = Long::class.java,
defaultValue = 0
)
)

fun clearLoginScope() {
prefContext.clear(DOMAIN_SCOPE_LOGIN)
prefContext.clear(DOMAIN_SCOPE_CURRENT_VERSION)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.wafflestudio.snutt2.data.vacancy_noti

import com.wafflestudio.snutt2.lib.network.dto.core.LectureDto
import kotlinx.coroutines.flow.StateFlow

interface VacancyRepository {
val firstVacancyVisit: StateFlow<Boolean>

val vacancyBannerOpenTime: StateFlow<Long>

suspend fun getVacancyLectures(): List<LectureDto>

suspend fun addVacancyLecture(lectureId: String)

suspend fun removeVacancyLecture(lectureId: String)

suspend fun setVacancyVisited()

suspend fun updateVacancyBannerOpenTime()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.wafflestudio.snutt2.data.vacancy_noti

import com.wafflestudio.snutt2.data.SNUTTStorage
import com.wafflestudio.snutt2.lib.network.SNUTTRestApi
import com.wafflestudio.snutt2.lib.network.dto.core.LectureDto
import kotlinx.coroutines.flow.StateFlow
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class VacancyRepositoryImpl @Inject constructor(
private val api: SNUTTRestApi,
private val storage: SNUTTStorage,
) : VacancyRepository {

override val firstVacancyVisit = storage.firstVacancyVisit.asStateFlow()

override val vacancyBannerOpenTime: StateFlow<Long> = storage.vacancyBannerOpenTime.asStateFlow()

override suspend fun getVacancyLectures(): List<LectureDto> {
return api._getVacancyLectures().lectures
}

override suspend fun addVacancyLecture(lectureId: String) {
api._postVacancyLecture(lectureId)
}

override suspend fun removeVacancyLecture(lectureId: String) {
api._deleteVacancyLecture(lectureId)
}

override suspend fun setVacancyVisited() {
storage.firstVacancyVisit.update(false)
}

override suspend fun updateVacancyBannerOpenTime() {
storage.vacancyBannerOpenTime.update(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.wafflestudio.snutt2.data.tables.TableRepository
import com.wafflestudio.snutt2.data.tables.TableRepositoryImpl
import com.wafflestudio.snutt2.data.user.UserRepository
import com.wafflestudio.snutt2.data.user.UserRepositoryImpl
import com.wafflestudio.snutt2.data.vacancy_noti.VacancyRepository
import com.wafflestudio.snutt2.data.vacancy_noti.VacancyRepositoryImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -38,4 +40,7 @@ abstract class RepositoryModule {

@Binds
abstract fun bindsUserRepository(impl: UserRepositoryImpl): UserRepository

@Binds
abstract fun bindsVacancyRepository(impl: VacancyRepositoryImpl): VacancyRepository
}
Loading

0 comments on commit 967ff99

Please sign in to comment.