Skip to content

Commit

Permalink
refactor(auth): make AppUser a proper sealed class
Browse files Browse the repository at this point in the history
  • Loading branch information
outadoc committed Apr 23, 2023
1 parent 79ca0eb commit 59e3b2a
Show file tree
Hide file tree
Showing 15 changed files with 152 additions and 113 deletions.
24 changes: 18 additions & 6 deletions app/src/main/java/fr/outadoc/justchatting/di/MainModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,26 @@ val mainModule = module {
install(Auth) {
bearer {
loadTokens {
get<PreferenceRepository>()
val appUser = get<PreferenceRepository>()
.currentPreferences.first()
.appUser
.helixToken?.let { token ->
BearerTokens(
accessToken = token,
refreshToken = "",
)

when (appUser) {
is AppUser.LoggedIn -> {
BearerTokens(
accessToken = appUser.token,
refreshToken = "",
)
}

is AppUser.NotValidated -> {
BearerTokens(
accessToken = appUser.token,
refreshToken = "",
)
}

is AppUser.NotLoggedIn -> null
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fr.outadoc.justchatting.component.chatapi.domain.repository

import fr.outadoc.justchatting.component.chatapi.domain.model.OAuthAppCredentials
import fr.outadoc.justchatting.component.chatapi.domain.model.ValidationResponse
import fr.outadoc.justchatting.component.preferences.data.AppUser
import fr.outadoc.justchatting.component.preferences.domain.PreferenceRepository
import fr.outadoc.justchatting.component.twitch.http.api.IdApi
import kotlinx.coroutines.Dispatchers
Expand All @@ -26,10 +27,15 @@ class AuthRepository(

suspend fun revokeToken() {
withContext(Dispatchers.IO) {
val prefs = preferencesRepository.currentPreferences.first()
val user = preferencesRepository
.currentPreferences.first()
.appUser

if (user !is AppUser.LoggedIn) return@withContext

api.revokeToken(
clientId = oAuthAppCredentials.clientId,
token = prefs.appUser.helixToken ?: return@withContext,
token = user.token,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import fr.outadoc.justchatting.component.chatapi.domain.model.User
import fr.outadoc.justchatting.component.chatapi.domain.repository.datasource.FollowedChannelsDataSource
import fr.outadoc.justchatting.component.chatapi.domain.repository.datasource.FollowedStreamsDataSource
import fr.outadoc.justchatting.component.chatapi.domain.repository.datasource.SearchChannelsDataSource
import fr.outadoc.justchatting.component.preferences.data.AppUser
import fr.outadoc.justchatting.component.preferences.domain.PreferenceRepository
import fr.outadoc.justchatting.component.twitch.http.api.HelixApi
import fr.outadoc.justchatting.component.twitch.utils.map
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -68,7 +70,10 @@ class TwitchRepositoryImpl(
}

override suspend fun loadFollowedStreams(): Flow<PagingData<Stream>> {
val prefs = preferencesRepository.currentPreferences.first()
val appUser: AppUser = preferencesRepository.currentPreferences.first().appUser

if (appUser !is AppUser.LoggedIn) return emptyFlow()

val pager = Pager(
config = PagingConfig(
pageSize = 30,
Expand All @@ -78,7 +83,7 @@ class TwitchRepositoryImpl(
),
pagingSourceFactory = {
FollowedStreamsDataSource(
userId = prefs.appUser.id,
userId = appUser.userId,
helixApi = helix,
)
},
Expand Down Expand Up @@ -113,7 +118,10 @@ class TwitchRepositoryImpl(
}

override suspend fun loadFollowedChannels(): Flow<PagingData<ChannelFollow>> {
val prefs = preferencesRepository.currentPreferences.first()
val appUser: AppUser = preferencesRepository.currentPreferences.first().appUser

if (appUser !is AppUser.LoggedIn) return emptyFlow()

val pager = Pager(
config = PagingConfig(
pageSize = 40,
Expand All @@ -123,7 +131,7 @@ class TwitchRepositoryImpl(
),
pagingSourceFactory = {
FollowedChannelsDataSource(
userId = prefs.appUser.id,
userId = appUser.userId,
helixApi = helix,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fr.outadoc.justchatting.component.chatapi.common.ChatEvent
import fr.outadoc.justchatting.component.chatapi.common.ConnectionStatus
import fr.outadoc.justchatting.component.chatapi.common.handler.ChatCommandHandlerFactory
import fr.outadoc.justchatting.component.chatapi.common.handler.ChatEventHandler
import fr.outadoc.justchatting.component.preferences.data.AppUser
import fr.outadoc.justchatting.component.preferences.domain.PreferenceRepository
import fr.outadoc.justchatting.component.twitch.R
import fr.outadoc.justchatting.component.twitch.websocket.Defaults
Expand Down Expand Up @@ -147,10 +148,14 @@ class LoggedInChatWebSocket(
httpClient.webSocket(ENDPOINT) {
logDebug<LoggedInChatWebSocket> { "Socket open, logging in" }

val prefs = preferencesRepository.currentPreferences.first()
val appUser = preferencesRepository
.currentPreferences.first()
.appUser

send("PASS oauth:${prefs.appUser.helixToken}")
send("NICK ${prefs.appUser.login}")
if (appUser !is AppUser.LoggedIn) return@webSocket

send("PASS oauth:${appUser.token}")
send("NICK ${appUser.userLogin}")
send("CAP REQ :twitch.tv/tags twitch.tv/commands")
send("JOIN #$channelLogin")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fr.outadoc.justchatting.component.chatapi.common.ConnectionStatus
import fr.outadoc.justchatting.component.chatapi.common.handler.ChatCommandHandlerFactory
import fr.outadoc.justchatting.component.chatapi.common.handler.ChatEventHandler
import fr.outadoc.justchatting.component.chatapi.common.pubsub.PubSubPluginsProvider
import fr.outadoc.justchatting.component.preferences.data.AppUser
import fr.outadoc.justchatting.component.preferences.domain.PreferenceRepository
import fr.outadoc.justchatting.component.twitch.websocket.Defaults
import fr.outadoc.justchatting.component.twitch.websocket.pubsub.client.model.PubSubClientMessage
Expand Down Expand Up @@ -117,9 +118,11 @@ class PubSubWebSocket(

private suspend fun listen() {
httpClient.webSocket(ENDPOINT) {
val helixToken: String =
preferencesRepository.currentPreferences.first().appUser.helixToken
?: error("User is not authenticated")
val appUser = preferencesRepository
.currentPreferences.first()
.appUser

if (appUser !is AppUser.LoggedIn) return@webSocket

logDebug<PubSubWebSocket> { "Socket open, sending the LISTEN message" }

Expand All @@ -129,7 +132,7 @@ class PubSubWebSocket(
data = PubSubClientMessage.Listen.Data(
topics = pubSubPluginsProvider.get()
.map { plugin -> plugin.getTopic(channelId) },
authToken = helixToken,
authToken = appUser.token,
),
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,15 @@ package fr.outadoc.justchatting.component.preferences.data

sealed class AppUser {

abstract val id: String?
abstract val login: String?
abstract val helixToken: String?

data class LoggedIn(
override val id: String,
override val login: String,
override val helixToken: String,
val userId: String,
val userLogin: String,
val token: String,
) : AppUser()

data class NotValidated(
override val helixToken: String,
) : AppUser() {
override val id: String? = null
override val login: String? = null
}
val token: String,
) : AppUser()

object NotLoggedIn : AppUser() {
override val id: String? = null
override val login: String? = null
override val helixToken: String? = null
}
object NotLoggedIn : AppUser()
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,25 @@ class SharedPrefsPreferenceRepository(
}

private fun AppPreferences.writeTo(prefs: MutablePreferences) {
prefs[USER_ID] = appUser.id ?: ""
prefs[USER_LOGIN] = appUser.login ?: ""
prefs[USER_TOKEN] = appUser.helixToken ?: ""
when (val user = appUser) {
is AppUser.LoggedIn -> {
prefs[USER_ID] = user.userId
prefs[USER_LOGIN] = user.userLogin
prefs[USER_TOKEN] = user.token
}

AppUser.NotLoggedIn -> {
prefs[USER_ID] = ""
prefs[USER_LOGIN] = ""
prefs[USER_TOKEN] = ""
}

is AppUser.NotValidated -> {
prefs[USER_ID] = ""
prefs[USER_LOGIN] = ""
prefs[USER_TOKEN] = user.token
}
}

prefs[CHAT_ACCESSIBILITY_TIMESTAMPS] = showTimestamps

Expand All @@ -56,20 +72,20 @@ class SharedPrefsPreferenceRepository(
}

private fun Preferences.parseUser(): AppUser {
val id = this[USER_ID]
val login = this[USER_LOGIN]
val helixToken = this[USER_TOKEN]
val userId = this[USER_ID]
val userLogin = this[USER_LOGIN]
val token = this[USER_TOKEN]

return if (helixToken != null) {
if (!id.isNullOrEmpty() && !login.isNullOrEmpty()) {
return if (token != null) {
if (!userId.isNullOrEmpty() && !userLogin.isNullOrEmpty()) {
AppUser.LoggedIn(
id = id,
login = login,
helixToken = helixToken,
userId = userId,
userLogin = userLogin,
token = token,
)
} else {
AppUser.NotValidated(
helixToken = helixToken,
token = token,
)
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ fun ChatMessagePreview(
inlineContent = inlineBadges,
showTimestamps = true,
appUser = AppUser.LoggedIn(
id = "123",
login = "outadoc",
helixToken = "",
userId = "123",
userLogin = "outadoc",
token = "",
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fun ChatMessageBody(
inlineContent: ImmutableMap<String, InlineTextContent>,
knownChatters: PersistentSet<Chatter>,
pronouns: ImmutableMap<Chatter, Pronoun>,
appUser: AppUser,
appUser: AppUser.LoggedIn,
backgroundHint: Color,
richEmbed: ChatEvent.RichEmbed?,
maxLines: Int = Int.MAX_VALUE,
Expand Down Expand Up @@ -90,7 +90,7 @@ fun ChatMessageBody(
body.inReplyTo?.let { inReplyTo ->
InReplyToMessage(
modifier = Modifier.padding(bottom = 8.dp),
appUserId = appUser.id,
appUserId = appUser.userId,
chatter = inReplyTo.chatter,
message = inReplyTo.message,
)
Expand Down Expand Up @@ -157,7 +157,7 @@ fun ChatMessageBody(
@Composable
@OptIn(ExperimentalTextApi::class)
fun ChatEvent.Message.Body.toAnnotatedString(
appUser: AppUser,
appUser: AppUser.LoggedIn,
inlineContent: ImmutableMap<String, InlineTextContent>,
knownChatters: PersistentSet<Chatter>,
pronouns: ImmutableMap<Chatter, Pronoun>,
Expand Down Expand Up @@ -274,7 +274,7 @@ private fun AnnotatedString.Builder.appendUrl(url: String, urlColor: Color) {
@OptIn(ExperimentalTextApi::class)
private fun AnnotatedString.Builder.appendMention(
chatter: Chatter,
appUser: AppUser,
appUser: AppUser.LoggedIn,
mentionBackground: Color,
mentionColor: Color,
) {
Expand All @@ -285,7 +285,7 @@ private fun AnnotatedString.Builder.appendMention(
) {
withStyle(
getMentionStyle(
mentioned = chatter.login == appUser.login,
mentioned = chatter.login == appUser.userLogin,
mentionBackground = mentionBackground,
mentionColor = mentionColor,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ fun PinnedMessageCardPreview(
PinnedMessageCard(
message = message,
appUser = AppUser.LoggedIn(
id = "",
login = "",
helixToken = "",
userId = "",
userLogin = "",
token = "",
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ fun UserNoticeMessagePreview(
inlineContent = inlineBadges,
showTimestamps = true,
appUser = AppUser.LoggedIn(
id = "123",
login = "outadoc",
helixToken = "",
userId = "123",
userLogin = "outadoc",
token = "",
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ class ChatViewModel(
// Note that this is the last message we've sent
val lastSentMessageInstant: Instant? =
messages.lastOrNull { message ->
message.body != null && message.body?.chatter?.id == state.appUser.id
message.body != null && message.body?.chatter?.id == state.appUser.userId
}?.timestamp

// Remember names of chatters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ class MainRouterViewModel(
is AppUser.NotValidated -> {
try {
val userInfo: ValidationResponse =
authRepository.validate(appUser.helixToken)
authRepository.validate(appUser.token)
?: throw InvalidClientIdException()

val validatedUser = AppUser.LoggedIn(
id = userInfo.userId,
login = userInfo.login,
helixToken = appUser.helixToken,
userId = userInfo.userId,
userLogin = userInfo.login,
token = appUser.token,
)

if (userInfo.clientId != oAuthAppCredentials.clientId) {
Expand Down Expand Up @@ -121,7 +121,7 @@ class MainRouterViewModel(
preferencesRepository.updatePreferences { prefs ->
prefs.copy(
appUser = AppUser.NotValidated(
helixToken = deeplink.token,
token = deeplink.token,
),
)
}
Expand Down
Loading

0 comments on commit 59e3b2a

Please sign in to comment.