Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@ RobotConf/OpenFeedback-Info.plist
RobotConf/GoogleService-Info.plist
apollo-ios-cli
*.xcuserstate

firebase-debug.log
2 changes: 2 additions & 0 deletions shared/data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ apollo {
@OptIn(ApolloExperimental::class)
generateDataBuilders.set(true)
mapScalar("GraphQLLocalDateTime", "kotlinx.datetime.LocalDateTime", "com.apollographql.adapter.datetime.KotlinxLocalDateTimeAdapter")
mapScalar("GraphQLInstant", "kotlinx.datetime.Instant", "fr.androidmakers.store.graphql.KotlinxInstantAdapter")
mapScalarToKotlinString("Markdown")

@OptIn(ApolloExperimental::class)
plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin:${libs.versions.apollo.cache.get()}") {
Expand Down
15 changes: 15 additions & 0 deletions shared/data/src/commonMain/graphql/feed.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
query GetFeedMessages {
feedItemsConnection(first: 1000) {
nodes {
...FeedMessageDetails
}
}
}

fragment FeedMessageDetails on FeedItem {
id
type
title
body
createdAt
}
14 changes: 12 additions & 2 deletions shared/data/src/commonMain/graphql/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ enum ConferenceField {
DAYS
}

enum FeedItemType {
INFO

ALERT

ANNOUNCEMENT
}

enum LinkType {
YouTube

Expand Down Expand Up @@ -207,11 +215,13 @@ type FeatureFlags {
type FeedItem {
id: ID!

type: FeedItemType!

createdAt: GraphQLInstant!

title: String!

markdown: Markdown!
body: Markdown!
}

type FeedItemFailure {
Expand Down Expand Up @@ -463,7 +473,7 @@ input ConferenceOrderBy {
input FeedItemInput {
title: String

markdown: Markdown
body: Markdown
}

input SessionOrderBy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.apollographql.apollo.api.http.HttpRequest
import com.apollographql.apollo.api.http.HttpResponse
import com.apollographql.apollo.network.http.HttpInterceptor
import com.apollographql.apollo.network.http.HttpInterceptorChain
import com.apollographql.apollo.network.ws.WebSocketNetworkTransport
import com.apollographql.cache.normalized.api.NormalizedCacheFactory
import com.apollographql.cache.normalized.memory.MemoryCacheFactory
import fr.androidmakers.domain.repo.UserRepository
Expand All @@ -24,9 +25,6 @@ fun ApolloClient(
return chain.proceed(
request.newBuilder()
.apply {
/**
*
*/
val token = getIdToken(userRepository)
if (token != null) {
addHeader("Authorization", "Bearer $token")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package fr.androidmakers.store.graphql

import com.apollographql.apollo.ApolloClient
import fr.androidmakers.domain.model.FeedItem
import fr.androidmakers.domain.repo.FeedRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.scan

class FeedGraphQLRepository(
private val apolloClient: ApolloClient,
) : FeedRepository {

override fun getFeedItems(): Flow<Result<List<FeedItem>>> {
return apolloClient.query(GetFeedMessagesQuery())
.cacheAndNetwork()
.map { result -> result.map { data -> data.feedItemsConnection.nodes.map { it.feedMessageDetails.toFeedItem() } } }

Check warning

Code scanning / detekt

Line detected, which is longer than the defined maximum line length in the code style. Warning

Line detected, which is longer than the defined maximum line length in the code style.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package fr.androidmakers.store.graphql

import fr.androidmakers.domain.model.FeedItem
import fr.androidmakers.domain.model.MessageType
import fr.androidmakers.store.graphql.fragment.FeedMessageDetails
import fr.androidmakers.store.graphql.type.FeedItemType

fun FeedMessageDetails.toFeedItem(): FeedItem {
return when (type) {
FeedItemType.ALERT -> FeedItem.Alert(
id = id,
title = title,
message = body,
)
else -> FeedItem.Message(
id = id,
type = when (type) {
FeedItemType.ANNOUNCEMENT -> MessageType.ANNOUNCEMENT
else -> MessageType.INFO
},
title = title,
body = body,
createdAt = createdAt,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package fr.androidmakers.store.graphql

import com.apollographql.apollo.api.Adapter
import com.apollographql.apollo.api.CustomScalarAdapters
import com.apollographql.apollo.api.json.JsonReader
import com.apollographql.apollo.api.json.JsonWriter
import kotlinx.datetime.Instant

val KotlinxInstantAdapter = object : Adapter<Instant> {
override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): Instant {
val str = reader.nextString() ?: throw IllegalStateException("Expected non-null Instant string")

Check warning

Code scanning / detekt

Use check() or error() instead of throwing an IllegalStateException. Warning

Use check() or error() instead of throwing an IllegalStateException.
return Instant.parse(str)
}

override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: Instant) {
writer.value(value.toString())
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package fr.androidmakers.store.mock

import fr.androidmakers.domain.model.FeedItem
import fr.androidmakers.domain.model.LocationInfo
import fr.androidmakers.domain.model.MessageType
import fr.androidmakers.domain.repo.FeedRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlin.time.Clock
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.days

class MockFeedRepository : FeedRepository {
override fun getFeedItems(): Flow<Result<List<FeedItem>>> {
val now = Clock.System.now()
return flowOf(
Result.success(
listOf(
Expand All @@ -16,41 +20,28 @@ class MockFeedRepository : FeedRepository {
title = "Room Change Alert",
message = "The Kotlin Workshop has moved from Room 3 to Room 7. Please update your schedule accordingly.",
),
FeedItem.Article(
id = "article-1",
category = "KEYNOTE",
timeAgo = "2h ago",
FeedItem.Message(
id = "message-1",
type = MessageType.ANNOUNCEMENT,
title = "Opening Keynote: The Future of Android Development",
description = "Join us for an exciting keynote session exploring the latest " +
body = "Join us for an exciting keynote session exploring the latest " +
"innovations in Android development, from Compose Multiplatform to AI-powered tools.",
imageUrl = "https://images.unsplash.com/photo-1540575467063-178a50c2df87?w=800",
categoryBadge = "KEYNOTE",
avatarUrls = listOf(
"https://i.pravatar.cc/150?img=1",
"https://i.pravatar.cc/150?img=2",
"https://i.pravatar.cc/150?img=3",
),
readMoreUrl = "https://androidmakers.droidcon.com",
createdAt = now - 2.hours,
),
FeedItem.Article(
id = "article-2",
category = "EVENT",
timeAgo = "5h ago",
FeedItem.Message(
id = "message-2",
type = MessageType.INFO,
title = "After-Hours Party",
description = "Don't miss tonight's networking event with drinks and live music.",
location = LocationInfo(
name = "Le Café des Makers",
time = "7:00 PM - 11:00 PM",
),
body = "Don't miss tonight's networking event with drinks and live music.",
createdAt = now - 5.hours,
),
FeedItem.Article(
id = "article-3",
category = "ANNOUNCEMENT",
timeAgo = "1d ago",
FeedItem.Message(
id = "message-3",
type = MessageType.ANNOUNCEMENT,
title = "Swag Alert: Limited Edition T-Shirts",
description = "Pick up your exclusive Android Makers t-shirt at the registration desk. " +
body = "Pick up your exclusive Android Makers t-shirt at the registration desk. " +
"Available while supplies last!",
thumbnailUrl = "https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=200",
createdAt = now - 1.days,
),
)
)
Expand Down
28 changes: 14 additions & 14 deletions shared/di/src/commonMain/kotlin/fr/androidmakers/di/DataModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,32 @@ import fr.androidmakers.domain.repo.UserRepository
import fr.androidmakers.domain.repo.VenueRepository
import fr.androidmakers.store.firebase.FirebaseUserRepository
import fr.androidmakers.store.graphql.FeatureFlagsGraphQLRepository
import fr.androidmakers.store.graphql.FeedGraphQLRepository
import fr.androidmakers.store.graphql.PartnersGraphQLRepository
import fr.androidmakers.store.graphql.RoomsGraphQLRepository
import fr.androidmakers.store.graphql.SessionsGraphQLRepository
import fr.androidmakers.store.graphql.SpeakersGraphQLRepository
import fr.androidmakers.store.graphql.VenueGraphQLRepository
import fr.androidmakers.store.local.BookmarksDataStoreRepository
import fr.androidmakers.store.local.ThemeDataStoreRepository
import fr.androidmakers.store.mock.MockFeedRepository
import fr.androidmakers.store.wear.WearMessagingRepository
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module


expect val dataPlatformModule: Module

val dataModule = module {
single<PartnersRepository> { PartnersGraphQLRepository(get()) }
single<RoomsRepository> { RoomsGraphQLRepository(get()) }
single<SessionsRepository> { SessionsGraphQLRepository(get()) }
single<SpeakersRepository> { SpeakersGraphQLRepository(get()) }
single<UserRepository> { FirebaseUserRepository() }
single<VenueRepository> { VenueGraphQLRepository(get()) }
single<FeatureFlagsRepository> { FeatureFlagsGraphQLRepository(get()) }

single<BookmarksRepository> { BookmarksDataStoreRepository(get()) }
single<ThemeRepository> { ThemeDataStoreRepository(get()) }
single<MessagingRepository> { WearMessagingRepository(get()) }
single<FeedRepository> { MockFeedRepository() }
singleOf(::BookmarksDataStoreRepository) bind BookmarksRepository::class
singleOf(::FeedGraphQLRepository) bind FeedRepository::class
singleOf(::FirebaseUserRepository) bind UserRepository::class
singleOf(::PartnersGraphQLRepository) bind PartnersRepository::class
singleOf(::RoomsGraphQLRepository) bind RoomsRepository::class
singleOf(::SessionsGraphQLRepository) bind SessionsRepository::class
singleOf(::SpeakersGraphQLRepository) bind SpeakersRepository::class
singleOf(::ThemeDataStoreRepository) bind ThemeRepository::class
singleOf(::VenueGraphQLRepository) bind VenueRepository::class
singleOf(::WearMessagingRepository) bind MessagingRepository::class
singleOf(::FeatureFlagsGraphQLRepository) bind FeatureFlagsRepository::class
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package fr.androidmakers.domain.model

import kotlinx.datetime.Instant

sealed interface FeedItem {
val id: String

Expand All @@ -9,6 +11,15 @@ sealed interface FeedItem {
val message: String,
) : FeedItem

data class Message(
override val id: String,
val type: MessageType,
val title: String,
val body: String,
val createdAt: Instant,
) : FeedItem

// Conservé pour les items enrichis côté client (venues, floor plan)
data class Article(
override val id: String,
val category: String,
Expand All @@ -19,11 +30,15 @@ sealed interface FeedItem {
val thumbnailUrl: String? = null,
val categoryBadge: String? = null,
val location: LocationInfo? = null,
val avatarUrls: List<String> = emptyList(),
val readMoreUrl: String? = null,
) : FeedItem
}

enum class MessageType {
INFO,
ALERT,
ANNOUNCEMENT;
}

data class LocationInfo(
val name: String,
val time: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
<string name="remove_from_my_agenda">Retirer de mon agenda</string>

<string name="feed">Fil</string>
<string name="feed_read_more">Lire la suite</string>

<string name="about_links">Liens</string>
<string name="about_event_tagline">Deux jours dédiés à Android à Paris</string>
Expand Down
3 changes: 1 addition & 2 deletions shared/ui/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@
<string name="remove_from_my_agenda">Remove from My Agenda</string>

<string name="feed">Feed</string>
<string name="feed_read_more">Read More</string>
<string name="feed_category_venue">VENUE</string>
<string name="feed_category_venue">VENUE</string>
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The indentation on this line is inconsistent with the surrounding XML (missing the usual leading spaces). If the project runs XML formatting/lint checks, this can create noisy diffs or even fail formatting checks; please align it with the rest of the file’s indentation.

Suggested change
<string name="feed_category_venue">VENUE</string>
<string name="feed_category_venue">VENUE</string>

Copilot uses AI. Check for mistakes.
<string name="feed_category_event">EVENT</string>
<string name="feed_floor_plan">Floor Plan</string>

Expand Down
Loading
Loading