Skip to content

Commit 38cc804

Browse files
2 parents 73c6cc4 + 50c120e commit 38cc804

File tree

6 files changed

+152
-32
lines changed

6 files changed

+152
-32
lines changed

app/src/main/java/awais/instagrabber/models/HighlightModel.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package awais.instagrabber.models
22

33
import awais.instagrabber.utils.TextUtils
4-
import java.util.*
54

65
data class HighlightModel(
7-
val title: String?,
8-
val id: String,
9-
val thumbnailUrl: String,
10-
val timestamp: Long,
11-
val mediaCount: Int
6+
val title: String? = null,
7+
val id: String = "",
8+
val thumbnailUrl: String = "",
9+
val timestamp: Long = 0,
10+
val mediaCount: Int = 0,
1211
) {
1312
val dateTime: String
1413
get() = TextUtils.epochSecondToString(timestamp)

app/src/main/java/awais/instagrabber/models/StoryModel.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import awais.instagrabber.models.stickers.*
55
import java.io.Serializable
66

77
data class StoryModel(
8-
val storyMediaId: String?,
9-
val storyUrl: String?,
10-
var thumbnail: String?,
11-
val itemType: MediaItemType?,
12-
val timestamp: Long,
13-
val username: String?,
14-
val userId: Long,
15-
val canReply: Boolean
8+
val storyMediaId: String? = null,
9+
val storyUrl: String? = null,
10+
var thumbnail: String? = null,
11+
val itemType: MediaItemType? = null,
12+
val timestamp: Long = 0,
13+
val username: String? = null,
14+
val userId: Long = 0,
15+
val canReply: Boolean = false,
1616
) : Serializable {
1717
var videoUrl: String? = null
1818
var tappableShortCode: String? = null

app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import awais.instagrabber.db.entities.Favorite
88
import awais.instagrabber.db.repositories.AccountRepository
99
import awais.instagrabber.db.repositories.FavoriteRepository
1010
import awais.instagrabber.managers.DirectMessagesManager
11+
import awais.instagrabber.models.HighlightModel
1112
import awais.instagrabber.models.Resource
13+
import awais.instagrabber.models.StoryModel
1214
import awais.instagrabber.models.enums.FavoriteType
15+
import awais.instagrabber.repositories.requests.StoryViewerOptions
1316
import awais.instagrabber.repositories.responses.User
1417
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
1518
import awais.instagrabber.utils.ControlledRunner
@@ -22,7 +25,7 @@ class ProfileFragmentViewModel(
2225
state: SavedStateHandle,
2326
userRepository: UserRepository,
2427
friendshipRepository: FriendshipRepository,
25-
storiesRepository: StoriesRepository,
28+
private val storiesRepository: StoriesRepository,
2629
mediaRepository: MediaRepository,
2730
graphQLRepository: GraphQLRepository,
2831
accountRepository: AccountRepository,
@@ -61,27 +64,21 @@ class ProfileFragmentViewModel(
6164

6265
private val profileFetchControlledRunner = ControlledRunner<User?>()
6366
val profile: LiveData<Resource<User?>> = currentUserAndStateUsernameLiveData.switchMap {
64-
val (userResource, stateUsernameResource) = it
67+
val (currentUserResource, stateUsernameResource) = it
6568
liveData<Resource<User?>>(context = viewModelScope.coroutineContext + ioDispatcher) {
66-
if (userResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) {
69+
if (currentUserResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) {
6770
emit(Resource.loading(null))
6871
return@liveData
6972
}
70-
val user = userResource.data
73+
val currentUser = currentUserResource.data
7174
val stateUsername = stateUsernameResource.data
7275
if (stateUsername.isNullOrBlank()) {
73-
emit(Resource.success(user))
76+
emit(Resource.success(currentUser))
7477
return@liveData
7578
}
7679
try {
7780
val fetchedUser = profileFetchControlledRunner.cancelPreviousThenRun {
78-
return@cancelPreviousThenRun if (user != null) {
79-
val tempUser = userRepository.getUsernameInfo(stateUsername) // logged in
80-
tempUser.friendshipStatus = userRepository.getUserFriendship(tempUser.pk)
81-
return@cancelPreviousThenRun tempUser
82-
} else {
83-
graphQLRepository.fetchUser(stateUsername) // anonymous
84-
}
81+
return@cancelPreviousThenRun fetchUser(currentUser, userRepository, stateUsername, graphQLRepository)
8582
}
8683
emit(Resource.success(fetchedUser))
8784
if (fetchedUser != null) {
@@ -94,6 +91,81 @@ class ProfileFragmentViewModel(
9491
}
9592
}
9693

94+
private val storyFetchControlledRunner = ControlledRunner<List<StoryModel>?>()
95+
val userStories: LiveData<Resource<List<StoryModel>?>> = profile.switchMap { userResource ->
96+
liveData<Resource<List<StoryModel>?>>(context = viewModelScope.coroutineContext + ioDispatcher) {
97+
// don't fetch if not logged in
98+
if (isLoggedIn.value != true) {
99+
emit(Resource.success(null))
100+
return@liveData
101+
}
102+
if (userResource.status == Resource.Status.LOADING) {
103+
emit(Resource.loading(null))
104+
return@liveData
105+
}
106+
val user = userResource.data
107+
if (user == null) {
108+
emit(Resource.success(null))
109+
return@liveData
110+
}
111+
try {
112+
val fetchedStories = storyFetchControlledRunner.cancelPreviousThenRun { fetchUserStory(user) }
113+
emit(Resource.success(fetchedStories))
114+
} catch (e: Exception) {
115+
emit(Resource.error(e.message, null))
116+
Log.e(TAG, "fetching story: ", e)
117+
}
118+
}
119+
}
120+
121+
private val highlightsFetchControlledRunner = ControlledRunner<List<HighlightModel>?>()
122+
val userHighlights: LiveData<Resource<List<HighlightModel>?>> = profile.switchMap { userResource ->
123+
liveData<Resource<List<HighlightModel>?>>(context = viewModelScope.coroutineContext + ioDispatcher) {
124+
// don't fetch if not logged in
125+
if (isLoggedIn.value != true) {
126+
emit(Resource.success(null))
127+
return@liveData
128+
}
129+
if (userResource.status == Resource.Status.LOADING) {
130+
emit(Resource.loading(null))
131+
return@liveData
132+
}
133+
val user = userResource.data
134+
if (user == null) {
135+
emit(Resource.success(null))
136+
return@liveData
137+
}
138+
try {
139+
val fetchedHighlights = highlightsFetchControlledRunner.cancelPreviousThenRun { fetchUserHighlights(user) }
140+
emit(Resource.success(fetchedHighlights))
141+
} catch (e: Exception) {
142+
emit(Resource.error(e.message, null))
143+
Log.e(TAG, "fetching story: ", e)
144+
}
145+
}
146+
}
147+
148+
private suspend fun fetchUser(
149+
currentUser: User?,
150+
userRepository: UserRepository,
151+
stateUsername: String,
152+
graphQLRepository: GraphQLRepository
153+
) = if (currentUser != null) {
154+
// logged in
155+
val tempUser = userRepository.getUsernameInfo(stateUsername)
156+
tempUser.friendshipStatus = userRepository.getUserFriendship(tempUser.pk)
157+
tempUser
158+
} else {
159+
// anonymous
160+
graphQLRepository.fetchUser(stateUsername)
161+
}
162+
163+
private suspend fun fetchUserStory(fetchedUser: User): List<StoryModel> = storiesRepository.getUserStory(
164+
StoryViewerOptions.forUser(fetchedUser.pk, fetchedUser.fullName)
165+
)
166+
167+
private suspend fun fetchUserHighlights(fetchedUser: User): List<HighlightModel> = storiesRepository.fetchHighlights(fetchedUser.pk)
168+
97169
private suspend fun checkAndInsertFavorite(fetchedUser: User) {
98170
try {
99171
val favorite = favoriteRepository.getFavorite(fetchedUser.username, FavoriteType.USER)

app/src/main/java/awais/instagrabber/webservices/StoriesRepository.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import org.json.JSONArray
1919
import org.json.JSONObject
2020
import java.util.*
2121

22-
class StoriesRepository(private val service: StoriesService) {
22+
open class StoriesRepository(private val service: StoriesService) {
2323

2424
suspend fun fetch(mediaId: Long): StoryModel {
2525
val response = service.fetch(mediaId)
@@ -99,7 +99,7 @@ class StoriesRepository(private val service: StoriesService) {
9999
return sort(feedStoryModels)
100100
}
101101

102-
suspend fun fetchHighlights(profileId: Long): List<HighlightModel> {
102+
open suspend fun fetchHighlights(profileId: Long): List<HighlightModel> {
103103
val response = service.fetchHighlights(profileId)
104104
val highlightsReel = JSONObject(response).getJSONArray("tray")
105105
val length = highlightsReel.length()
@@ -150,7 +150,7 @@ class StoriesRepository(private val service: StoriesService) {
150150
return ArchiveFetchResponse(highlightModels, data.getBoolean("more_available"), data.getString("max_id"))
151151
}
152152

153-
suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> {
153+
open suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> {
154154
val url = buildUrl(options) ?: return emptyList()
155155
val response = service.getUserStory(url)
156156
val isLocOrHashtag = options.type == StoryViewerOptions.Type.LOCATION || options.type == StoryViewerOptions.Type.HASHTAG

app/src/test/java/awais/instagrabber/common/Adapters.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ open class UserServiceAdapter : UserService {
1717
TODO("Not yet implemented")
1818
}
1919

20-
override suspend fun getUserFriendship(uid: Long): FriendshipStatus {
21-
TODO("Not yet implemented")
22-
}
20+
override suspend fun getUserFriendship(uid: Long): FriendshipStatus = FriendshipStatus()
2321

2422
override suspend fun search(timezoneOffset: Float, query: String): UserSearchResponse {
2523
TODO("Not yet implemented")

app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import awais.instagrabber.db.entities.Favorite
1111
import awais.instagrabber.db.repositories.AccountRepository
1212
import awais.instagrabber.db.repositories.FavoriteRepository
1313
import awais.instagrabber.getOrAwaitValue
14+
import awais.instagrabber.models.HighlightModel
1415
import awais.instagrabber.models.Resource
16+
import awais.instagrabber.models.StoryModel
1517
import awais.instagrabber.models.enums.FavoriteType
18+
import awais.instagrabber.repositories.requests.StoryViewerOptions
1619
import awais.instagrabber.repositories.responses.FriendshipStatus
1720
import awais.instagrabber.repositories.responses.User
1821
import awais.instagrabber.webservices.*
@@ -279,6 +282,54 @@ internal class ProfileFragmentViewModelTest {
279282
while (profile.status == Resource.Status.LOADING) {
280283
profile = viewModel.profile.getOrAwaitValue()
281284
}
285+
assertEquals(true, viewModel.isFavorite.getOrAwaitValue())
282286
assertTrue(updateFavoriteCalled)
283287
}
288+
289+
290+
@ExperimentalCoroutinesApi
291+
@Test
292+
fun `should fetch user stories and highlights when logged in`() {
293+
val state = SavedStateHandle(
294+
mutableMapOf<String, Any?>(
295+
"username" to testPublicUser.username
296+
)
297+
)
298+
val testUserStories = listOf(StoryModel())
299+
val testUserHighlights = listOf(HighlightModel())
300+
val userRepository = object : UserRepository(UserServiceAdapter()) {
301+
override suspend fun getUsernameInfo(username: String): User = testPublicUser
302+
}
303+
val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) {
304+
override suspend fun getUserStory(options: StoryViewerOptions): List<StoryModel> = testUserStories
305+
override suspend fun fetchHighlights(profileId: Long): List<HighlightModel> = testUserHighlights
306+
}
307+
val viewModel = ProfileFragmentViewModel(
308+
state,
309+
userRepository,
310+
FriendshipRepository(FriendshipServiceAdapter()),
311+
storiesRepository,
312+
MediaRepository(MediaServiceAdapter()),
313+
GraphQLRepository(GraphQLServiceAdapter()),
314+
AccountRepository(AccountDataSource(AccountDaoAdapter())),
315+
FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())),
316+
coroutineScope.dispatcher,
317+
)
318+
viewModel.setCurrentUser(Resource.success(User()))
319+
assertEquals(true, viewModel.isLoggedIn.getOrAwaitValue())
320+
var profile = viewModel.profile.getOrAwaitValue()
321+
while (profile.status == Resource.Status.LOADING) {
322+
profile = viewModel.profile.getOrAwaitValue()
323+
}
324+
var userStories = viewModel.userStories.getOrAwaitValue()
325+
while (userStories.status == Resource.Status.LOADING) {
326+
userStories = viewModel.userStories.getOrAwaitValue()
327+
}
328+
assertEquals(testUserStories, userStories.data)
329+
var userHighlights = viewModel.userHighlights.getOrAwaitValue()
330+
while (userHighlights.status == Resource.Status.LOADING) {
331+
userHighlights = viewModel.userHighlights.getOrAwaitValue()
332+
}
333+
assertEquals(testUserHighlights, userHighlights.data)
334+
}
284335
}

0 commit comments

Comments
 (0)