├── app
├── .gitignore
├── keystore
│ ├── praxis-debug.jks
│ └── praxis-release.jks
├── src
│ ├── main
│ │ ├── ic_launcher-playstore.png
│ │ ├── res
│ │ │ ├── font
│ │ │ │ ├── unisans_bold.otf
│ │ │ │ ├── unisans_book.otf
│ │ │ │ ├── unisans_thin.otf
│ │ │ │ ├── unisans_heavy.otf
│ │ │ │ ├── unisans_italic.otf
│ │ │ │ ├── unisans_light.otf
│ │ │ │ ├── unisans_regular.otf
│ │ │ │ ├── unisans_semibold.otf
│ │ │ │ ├── unisans_bold_italic.otf
│ │ │ │ ├── unisans_book_italic.otf
│ │ │ │ ├── unisans_thin_italic.otf
│ │ │ │ ├── unisans_heavy_italic.otf
│ │ │ │ ├── unisans_light_italic.otf
│ │ │ │ ├── unisans_regular_italic.ttf
│ │ │ │ └── unisans_semibold_italic.otf
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── recentlogo.png
│ │ │ │ ├── welcomelogo.webp
│ │ │ │ ├── ic_hide_password.png
│ │ │ │ ├── ic_show_password.png
│ │ │ │ ├── background_image_dark.png
│ │ │ │ ├── background_image_light.png
│ │ │ │ └── discord_welcome_header_light.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── drawable-mdpi
│ │ │ │ └── welcomelogo.webp
│ │ │ ├── drawable-xhdpi
│ │ │ │ └── welcomelogo.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ └── welcomelogo.webp
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ └── welcomelogo.webp
│ │ │ ├── values
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── themes.xml
│ │ │ ├── drawable
│ │ │ │ ├── splash_image.xml
│ │ │ │ ├── splash_image_dark.xml
│ │ │ │ ├── ic_add.xml
│ │ │ │ ├── ic_baseline_circle_24.xml
│ │ │ │ ├── ic_chat_bubble.xml
│ │ │ │ ├── ic_baseline_info_24.xml
│ │ │ │ ├── ic_baseline_laptop_24.xml
│ │ │ │ ├── ic_baseline_image_24.xml
│ │ │ │ ├── ic_invite.xml
│ │ │ │ ├── ic_baseline_security_24.xml
│ │ │ │ ├── ic_baseline_edit_24.xml
│ │ │ │ ├── ic_baseline_subscriptions_24.xml
│ │ │ │ ├── ic_baseline_vpn_key_24.xml
│ │ │ │ ├── ic_notifications.xml
│ │ │ │ ├── ic_baseline_accessibility_new_24.xml
│ │ │ │ ├── ic_refresh.xml
│ │ │ │ ├── ic_baseline_mic_24.xml
│ │ │ │ ├── ic_send_rounded.xml
│ │ │ │ ├── ic_baseline_person_add_alt_1_24.xml
│ │ │ │ ├── ic_outline_cancel.xml
│ │ │ │ ├── ic_boost.xml
│ │ │ │ ├── ic_baseline_notification_important_24.xml
│ │ │ │ ├── ic_baseline_account_circle_24.xml
│ │ │ │ ├── ic_baseline_contact_support_24.xml
│ │ │ │ ├── ic_baseline_account_box_24.xml
│ │ │ │ ├── ic_baseline_card_giftcard_24.xml
│ │ │ │ ├── ic_baseline_color_lens_24.xml
│ │ │ │ ├── ic_nitro.xml
│ │ │ │ ├── ic_hashtag_solid.xml
│ │ │ │ ├── ic_nitro_verified.xml
│ │ │ │ ├── ic_baseline_language_24.xml
│ │ │ │ ├── ic_baseline_qr_code_24.xml
│ │ │ │ ├── ic_baseline_settings_applications_24.xml
│ │ │ │ ├── dark_app_logo.xml
│ │ │ │ ├── light_app_logo.xml
│ │ │ │ ├── ic_discord_icon.xml
│ │ │ │ ├── ic_launcher_foreground.xml
│ │ │ │ ├── ic_emoji_2.xml
│ │ │ │ ├── ic_emoji_3.xml
│ │ │ │ ├── ic_emoji_5.xml
│ │ │ │ ├── ic_emoji_4.xml
│ │ │ │ └── ic_emoji_1.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ └── values-night
│ │ │ │ └── themes.xml
│ │ ├── java
│ │ │ └── dev
│ │ │ │ └── baseio
│ │ │ │ └── discordjetpackcompose
│ │ │ │ ├── ui
│ │ │ │ ├── utils
│ │ │ │ │ └── Typealias.kt
│ │ │ │ ├── routes
│ │ │ │ │ ├── dashboard
│ │ │ │ │ │ ├── notifications
│ │ │ │ │ │ │ ├── models
│ │ │ │ │ │ │ │ ├── FrequencyType.kt
│ │ │ │ │ │ │ │ ├── NotificationSettingsType.kt
│ │ │ │ │ │ │ │ └── OverrideItem.kt
│ │ │ │ │ │ │ └── components
│ │ │ │ │ │ │ │ ├── SectionItem.kt
│ │ │ │ │ │ │ │ ├── SubtitleAppBar.kt
│ │ │ │ │ │ │ │ ├── MentionsSection.kt
│ │ │ │ │ │ │ │ └── NotificationFrequencySection.kt
│ │ │ │ │ │ ├── userSettings
│ │ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ │ ├── models
│ │ │ │ │ │ │ │ │ └── SettingsEntity.kt
│ │ │ │ │ │ │ │ ├── SettingsTitle.kt
│ │ │ │ │ │ │ │ └── SettingsAppBar.kt
│ │ │ │ │ │ │ ├── UserSettingsListItem.kt
│ │ │ │ │ │ │ └── UserSettingsList.kt
│ │ │ │ │ │ ├── serverinfo
│ │ │ │ │ │ │ └── components
│ │ │ │ │ │ │ │ ├── models
│ │ │ │ │ │ │ │ ├── ServerQuickAction.kt
│ │ │ │ │ │ │ │ └── ServerInfoAction.kt
│ │ │ │ │ │ │ │ └── ServerQuickActions.kt
│ │ │ │ │ │ ├── createServer
│ │ │ │ │ │ │ └── ServerTemplates.kt
│ │ │ │ │ │ ├── main
│ │ │ │ │ │ │ ├── dasboard
│ │ │ │ │ │ │ │ └── DashboardUtil.kt
│ │ │ │ │ │ │ └── chatscreen
│ │ │ │ │ │ │ │ └── ChatScreenContent.kt
│ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ └── OnlineIndicator.kt
│ │ │ │ │ │ ├── search
│ │ │ │ │ │ │ └── components
│ │ │ │ │ │ │ │ └── SearchSheetServerSlider.kt
│ │ │ │ │ │ ├── serverchannels
│ │ │ │ │ │ │ └── ServerChannelList.kt
│ │ │ │ │ │ └── DashboardRoute.kt
│ │ │ │ │ └── onboarding
│ │ │ │ │ │ ├── OnboardingRoute.kt
│ │ │ │ │ │ ├── commonui
│ │ │ │ │ │ ├── CenteredTitleSubtitle.kt
│ │ │ │ │ │ └── OnboardingScreensButton.kt
│ │ │ │ │ │ └── screens
│ │ │ │ │ │ └── register
│ │ │ │ │ │ └── RegistrationTypeSelector.kt
│ │ │ │ ├── theme
│ │ │ │ │ ├── Shape.kt
│ │ │ │ │ └── Surface.kt
│ │ │ │ └── components
│ │ │ │ │ ├── DiscordAppBar.kt
│ │ │ │ │ └── DiscordScaffold.kt
│ │ │ │ ├── DiscordApp.kt
│ │ │ │ ├── di
│ │ │ │ └── NavigationModule.kt
│ │ │ │ ├── viewmodels
│ │ │ │ ├── LoginScreenViewModel.kt
│ │ │ │ ├── RegistrationViewModel.kt
│ │ │ │ └── FriendsViewModel.kt
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ └── androidTest
│ │ └── java
│ │ └── dev
│ │ └── baseio
│ │ └── discordjetpackcompose
│ │ └── ui
│ │ └── utils
│ │ └── ExtensionUtils.kt
└── proguard-rules.pro
├── data
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── dev
│ │ │ └── baseio
│ │ │ └── discordjetpackcompose
│ │ │ ├── mappers
│ │ │ ├── EntityMapper.kt
│ │ │ ├── CountryMapper.kt
│ │ │ └── DiscordMessageMapper.kt
│ │ │ ├── di
│ │ │ ├── dispatcher
│ │ │ │ ├── CoroutineDispatcherProvider.kt
│ │ │ │ └── RealCoroutineDispatcherProvider.kt
│ │ │ ├── DispatcherModule.kt
│ │ │ ├── DataMappersModule.kt
│ │ │ ├── DataModule.kt
│ │ │ └── RepositoryModule.kt
│ │ │ ├── local
│ │ │ ├── database
│ │ │ │ └── DiscordDatabase.kt
│ │ │ ├── model
│ │ │ │ └── DBDiscordMessage.kt
│ │ │ └── dao
│ │ │ │ └── DiscordMessageDao.kt
│ │ │ ├── models
│ │ │ └── Country.kt
│ │ │ ├── utils
│ │ │ ├── ExtensionUtils.kt
│ │ │ ├── NetworkUtils.kt
│ │ │ └── SampleData.kt
│ │ │ └── repositories
│ │ │ ├── CountryRepoImpl.kt
│ │ │ ├── FriendsRepoImpl.kt
│ │ │ ├── MessagesRepoImpl.kt
│ │ │ └── ServerRepoImpl.kt
│ ├── test
│ │ └── java
│ │ │ └── dev
│ │ │ └── baseio
│ │ │ └── slackclone
│ │ │ └── data
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── dev
│ │ └── baseio
│ │ └── slackclone
│ │ └── data
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle.kts
├── domain
├── .gitignore
├── src
│ ├── main
│ │ └── java
│ │ │ └── dev
│ │ │ └── baseio
│ │ │ └── discordjetpackcompose
│ │ │ ├── utils
│ │ │ └── Constants.kt
│ │ │ ├── entities
│ │ │ ├── NetworkState.kt
│ │ │ ├── message
│ │ │ │ ├── DiscordUrlMetaEntity.kt
│ │ │ │ └── DiscordMessageEntity.kt
│ │ │ ├── CountryEntity.kt
│ │ │ ├── UIState.kt
│ │ │ ├── search
│ │ │ │ └── SearchSheetListItemEntity.kt
│ │ │ ├── ChatUserEntity.kt
│ │ │ └── server
│ │ │ │ ├── ChannelEntity.kt
│ │ │ │ └── ServerEntity.kt
│ │ │ ├── repositories
│ │ │ ├── CountryRepo.kt
│ │ │ ├── FriendsRepo.kt
│ │ │ ├── ServerRepo.kt
│ │ │ └── MessagesRepo.kt
│ │ │ ├── usecases
│ │ │ ├── FetchFriendsUseCase.kt
│ │ │ ├── server
│ │ │ │ └── GetServerListUseCase.kt
│ │ │ ├── FetchCountriesUseCase.kt
│ │ │ ├── GetServerUseCase.kt
│ │ │ ├── FetchFriendSuggestionsUseCase.kt
│ │ │ ├── search
│ │ │ │ └── GetSearchSheetItemListUseCase.kt
│ │ │ ├── chat
│ │ │ │ ├── FetchUrlMetadataUseCase.kt
│ │ │ │ ├── SendMessageUseCase.kt
│ │ │ │ └── FetchMessagesUseCase.kt
│ │ │ └── BaseUseCase.kt
│ │ │ └── di
│ │ │ └── UseCaseModule.kt
│ └── test
│ │ └── java
│ │ └── dev
│ │ └── baseio
│ │ └── slackclone
│ │ └── domain
│ │ └── ExampleUnitTest.kt
└── build.gradle.kts
├── benchmark
├── .gitignore
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── dev
│ │ └── baseio
│ │ └── discordjetpackcompose
│ │ ├── baselineprof
│ │ └── BaselineProfileGenerator.kt
│ │ └── startup
│ │ └── StartupBenchmark.kt
└── build.gradle.kts
├── navigator
├── .gitignore
├── consumer-rules.pro
├── src
│ ├── main
│ │ ├── java
│ │ │ └── dev
│ │ │ │ └── baseio
│ │ │ │ └── discordjetpackcompose
│ │ │ │ └── navigator
│ │ │ │ ├── NavigationKeys.kt
│ │ │ │ ├── NavigationCommand.kt
│ │ │ │ ├── Screens.kt
│ │ │ │ ├── ComposeNavigator.kt
│ │ │ │ └── Navigator.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── dev
│ │ │ └── baseio
│ │ │ └── slackclone
│ │ │ └── navigator
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── dev
│ │ └── baseio
│ │ └── slackclone
│ │ └── navigator
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle.kts
├── art
├── discord_flow.gif
├── discord_chat_dark.png
├── discord_chat_light.png
├── discord_friends_dark.png
├── discord_invite_dark.png
├── discord_invite_light.png
├── discord_login_dark.png
├── discord_login_light.png
├── discord_welcome_dark.png
├── discord_chat_list_dark.png
├── discord_friends_light.png
├── discord_register_dark.png
├── discord_register_light.png
├── discord_welcome_light.png
├── discord_chat_list_light.png
├── discord_notification_dark.png
├── discord_create_server_dark.png
├── discord_create_server_light.png
├── discord_notification_light.png
├── discord_channel_members_dark.png
├── discord_channel_members_light.png
├── discord_password_manager_dialog_dark.png
└── discord_password_manager_dialog_light.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .github
├── codeowners
├── issue_template
│ ├── feature_request.md
│ ├── bug_report.md
│ └── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── build.yml
├── settings.gradle.kts
├── .gitignore
├── team-props
├── git-hooks
│ └── pre-commit.sh
└── git-hooks.gradle.kts
├── gradle.properties
└── gradlew.bat
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/benchmark/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/navigator/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/navigator/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/art/discord_flow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_flow.gif
--------------------------------------------------------------------------------
/art/discord_chat_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_chat_dark.png
--------------------------------------------------------------------------------
/art/discord_chat_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_chat_light.png
--------------------------------------------------------------------------------
/art/discord_friends_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_friends_dark.png
--------------------------------------------------------------------------------
/art/discord_invite_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_invite_dark.png
--------------------------------------------------------------------------------
/art/discord_invite_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_invite_light.png
--------------------------------------------------------------------------------
/art/discord_login_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_login_dark.png
--------------------------------------------------------------------------------
/art/discord_login_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_login_light.png
--------------------------------------------------------------------------------
/art/discord_welcome_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_welcome_dark.png
--------------------------------------------------------------------------------
/app/keystore/praxis-debug.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/keystore/praxis-debug.jks
--------------------------------------------------------------------------------
/art/discord_chat_list_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_chat_list_dark.png
--------------------------------------------------------------------------------
/art/discord_friends_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_friends_light.png
--------------------------------------------------------------------------------
/art/discord_register_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_register_dark.png
--------------------------------------------------------------------------------
/art/discord_register_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_register_light.png
--------------------------------------------------------------------------------
/art/discord_welcome_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_welcome_light.png
--------------------------------------------------------------------------------
/app/keystore/praxis-release.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/keystore/praxis-release.jks
--------------------------------------------------------------------------------
/art/discord_chat_list_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_chat_list_light.png
--------------------------------------------------------------------------------
/art/discord_notification_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_notification_dark.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/art/discord_create_server_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_create_server_dark.png
--------------------------------------------------------------------------------
/art/discord_create_server_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_create_server_light.png
--------------------------------------------------------------------------------
/art/discord_notification_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_notification_light.png
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_bold.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_book.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_book.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_thin.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_thin.otf
--------------------------------------------------------------------------------
/art/discord_channel_members_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_channel_members_dark.png
--------------------------------------------------------------------------------
/art/discord_channel_members_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_channel_members_light.png
--------------------------------------------------------------------------------
/.github/codeowners:
--------------------------------------------------------------------------------
1 | # Setting default codeowners to default as reviewers
2 | * @anmol92verma @aditya-bhawsar @pushpalroy @shubhamsinghshubham777
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_heavy.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_heavy.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_italic.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_light.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_regular.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_semibold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_semibold.otf
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/recentlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-hdpi/recentlogo.png
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_bold_italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_bold_italic.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_book_italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_book_italic.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_thin_italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_thin_italic.otf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/art/discord_password_manager_dialog_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_password_manager_dialog_dark.png
--------------------------------------------------------------------------------
/art/discord_password_manager_dialog_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/art/discord_password_manager_dialog_light.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/welcomelogo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-hdpi/welcomelogo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/welcomelogo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-mdpi/welcomelogo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/welcomelogo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-xhdpi/welcomelogo.webp
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_heavy_italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_heavy_italic.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_light_italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_light_italic.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_regular_italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_regular_italic.ttf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/welcomelogo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-xxhdpi/welcomelogo.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/welcomelogo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-xxxhdpi/welcomelogo.webp
--------------------------------------------------------------------------------
/app/src/main/res/font/unisans_semibold_italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/font/unisans_semibold_italic.otf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_hide_password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-hdpi/ic_hide_password.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_show_password.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-hdpi/ic_show_password.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/background_image_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-hdpi/background_image_dark.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/background_image_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-hdpi/background_image_light.png
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #5664FB
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/discord_welcome_header_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oianmol/DiscordJetpackCompose/master/app/src/main/res/drawable-hdpi/discord_welcome_header_light.png
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | // Root module
2 | include(":app")
3 |
4 | // Other modules
5 | include(":domain")
6 | include(":data")
7 | include(":navigator")
8 |
9 | include(":benchmark")
10 |
--------------------------------------------------------------------------------
/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/navigator/src/main/java/dev/baseio/discordjetpackcompose/navigator/NavigationKeys.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.navigator
2 |
3 | object NavigationKeys {
4 |
5 | val navigateChannel = "ChannelCreated"
6 |
7 | }
--------------------------------------------------------------------------------
/navigator/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/utils/Typealias.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.utils
2 |
3 | import dev.baseio.discordjetpackcompose.R
4 |
5 | typealias Strings = R.string
6 | typealias Drawables = R.drawable
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/utils/Constants.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.utils
2 |
3 | object Constants {
4 | const val MMLogoUrl = "https://upload.wikimedia.org/wikipedia/commons/0/00/Mutual_Mobile_Logo.png"
5 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/mappers/EntityMapper.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.mappers
2 |
3 | interface EntityMapper {
4 | fun mapToDomain(entity: Data): Domain
5 | fun mapToData(model: Domain): Data
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/notifications/models/FrequencyType.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.models
2 |
3 | enum class FrequencyType {
4 | ALL_MESSAGES,
5 | MENTIONS,
6 | NOTHING
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/notifications/models/NotificationSettingsType.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.models
2 |
3 | enum class NotificationSettingsType {
4 | SERVER,
5 | CATEGORY,
6 | CHANNEL
7 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Jul 07 15:58:10 IST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /captures
8 | .externalNativeBuild
9 | .idea
10 | *.aab
11 | .cxx
12 | */build
13 | */.gradle
14 | /buildSrc/build
15 | build
16 | local.properties
17 | /.idea/
18 | /buildSrc/build/
19 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/NetworkState.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities
2 |
3 | sealed class NetworkState {
4 | class Success(val data: T): NetworkState()
5 | class Failure(val throwable: Throwable): NetworkState()
6 | }
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/repositories/CountryRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import dev.baseio.discordjetpackcompose.entities.CountryEntity
4 |
5 | interface CountryRepo {
6 | suspend fun fetchCountriesFromAssetFile(): List?
7 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/data/src/test/java/dev/baseio/slackclone/data/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.data
2 |
3 | /**
4 | * Example local unit test, which will execute on the development machine (host).
5 | *
6 | * See [testing documentation](http://d.android.com/tools/testing).
7 | */
8 | class ExampleUnitTest {
9 | }
--------------------------------------------------------------------------------
/benchmark/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/message/DiscordUrlMetaEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities.message
2 |
3 | data class DiscordUrlMetaEntity(
4 | var title: String? = null,
5 | var desc: String? = null,
6 | var image: String? = null,
7 | var url: String? = null
8 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash_image_dark.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/CountryEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities
2 |
3 | data class CountryEntity(
4 | val alpha2: String = "",
5 | val currencyCode: String? = null,
6 | val localeForICU: String? = null,
7 | val name: String = "",
8 | val phoneCountryCode: String = ""
9 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/userSettings/components/models/SettingsEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.components.models
2 |
3 | data class SettingsEntity(
4 | val title: String,
5 | val icon: Int,
6 | val currentStatus: String? = null,
7 | val statusIcon: String? = null
8 | )
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/UIState.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities
2 |
3 | sealed class UIState {
4 | object Empty: UIState()
5 | object Loading: UIState()
6 | class Success(val data: T): UIState()
7 | class Failure(val throwable: Throwable): UIState()
8 | }
9 |
--------------------------------------------------------------------------------
/data/src/androidTest/java/dev/baseio/slackclone/data/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.data
2 |
3 |
4 | /**
5 | * Instrumented test, which will execute on an Android device.
6 | *
7 | * See [testing documentation](http://d.android.com/tools/testing).
8 | */
9 | class ExampleInstrumentedTest {
10 | fun useAppContext() {
11 | }
12 | }
--------------------------------------------------------------------------------
/team-props/git-hooks/pre-commit.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Running static analysis using ktlint..."
4 |
5 | # ktlintcheck
6 | # ./gradlew ktlintcheck --daemon
7 |
8 | status=$?
9 |
10 | if [ "$status" = 0 ] ; then
11 | echo "Static analysis found no problems."
12 | exit 0
13 | else
14 | echo 1>&2 "Static analysis found violations it could not fix."
15 | exit 1
16 | fi
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/FetchFriendsUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases
2 |
3 | import dev.baseio.discordjetpackcompose.repositories.FriendsRepo
4 | import javax.inject.Inject
5 |
6 | class FetchFriendsUseCase @Inject constructor(private val friendsRepo: FriendsRepo) {
7 | suspend operator fun invoke() = friendsRepo.fetchFriends()
8 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_circle_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_chat_bubble.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/repositories/FriendsRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import dev.baseio.discordjetpackcompose.entities.ChatUserEntity
4 | import kotlinx.coroutines.flow.Flow
5 |
6 | interface FriendsRepo {
7 | suspend fun fetchFriendSuggestions(): Flow>
8 | suspend fun fetchFriends(): Flow>
9 | }
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/server/GetServerListUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases.server
2 |
3 | import dev.baseio.discordjetpackcompose.repositories.ServerRepo
4 | import javax.inject.Inject
5 |
6 | class GetServerListUseCase @Inject constructor(private val serverRepo: ServerRepo) {
7 | suspend operator fun invoke() = serverRepo.getServerList()
8 | }
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/FetchCountriesUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases
2 |
3 | import dev.baseio.discordjetpackcompose.repositories.CountryRepo
4 | import javax.inject.Inject
5 |
6 | class FetchCountriesUseCase @Inject constructor(private val countryRepo: CountryRepo) {
7 | suspend operator fun invoke() = countryRepo.fetchCountriesFromAssetFile()
8 | }
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/GetServerUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases
2 |
3 | import dev.baseio.discordjetpackcompose.repositories.ServerRepo
4 | import javax.inject.Inject
5 |
6 | class GetServerUseCase @Inject constructor(private val serverRepo: ServerRepo) {
7 | suspend operator fun invoke(serverId: String) = serverRepo.getServer(serverId = serverId)
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/notifications/models/OverrideItem.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.models
2 |
3 | data class OverrideItem(
4 | val title: String,
5 | val subtitle: String?,
6 | val type: OverrideType,
7 | val frequencyType: FrequencyType
8 | )
9 |
10 | enum class OverrideType {
11 | CHANNEL,
12 | CATEGORY
13 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/di/dispatcher/CoroutineDispatcherProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di.dispatcher
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 |
5 | interface CoroutineDispatcherProvider {
6 | val main: CoroutineDispatcher
7 | val io: CoroutineDispatcher
8 | val default: CoroutineDispatcher
9 | val unconfirmed: CoroutineDispatcher
10 | }
11 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/FetchFriendSuggestionsUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases
2 |
3 | import dev.baseio.discordjetpackcompose.repositories.FriendsRepo
4 | import javax.inject.Inject
5 |
6 | class FetchFriendSuggestionsUseCase @Inject constructor(private val friendsRepo: FriendsRepo) {
7 | suspend operator fun invoke() = friendsRepo.fetchFriendSuggestions()
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val Shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
12 |
--------------------------------------------------------------------------------
/domain/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | id("org.jetbrains.kotlin.jvm")
4 | }
5 |
6 | java {
7 | sourceCompatibility = JavaVersion.VERSION_1_8
8 | targetCompatibility = JavaVersion.VERSION_1_8
9 | }
10 |
11 | dependencies {
12 | api(Lib.Kotlin.KT_STD)
13 | api(Lib.Async.COROUTINES)
14 | implementation("androidx.paging:paging-common-ktx:3.1.0")
15 | implementation(Lib.Di.hiltCore)
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/search/GetSearchSheetItemListUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases.search
2 |
3 | import dev.baseio.discordjetpackcompose.repositories.ServerRepo
4 | import javax.inject.Inject
5 |
6 | class GetSearchSheetItemListUseCase @Inject constructor(private val serverRepo: ServerRepo) {
7 | suspend operator fun invoke() = serverRepo.getSearchSheetItemList()
8 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_info_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_laptop_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_image_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_invite.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/serverinfo/components/models/ServerQuickAction.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.serverinfo.components.models
2 |
3 | import androidx.annotation.DrawableRes
4 | import androidx.compose.ui.graphics.Color
5 |
6 | data class ServerQuickAction(
7 | @DrawableRes val icon: Int,
8 | val iconTint: Color? = null,
9 | val label: String,
10 | val onClick: () -> Unit,
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_security_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/domain/src/test/java/dev/baseio/slackclone/domain/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.domain
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/navigator/src/test/java/dev/baseio/slackclone/navigator/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.navigator
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/DiscordApp.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 | import timber.log.Timber
6 | import timber.log.Timber.DebugTree
7 |
8 | @HiltAndroidApp
9 | class DiscordApp : Application() {
10 | override fun onCreate() {
11 | super.onCreate()
12 | if (BuildConfig.DEBUG) {
13 | Timber.plant(DebugTree())
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_edit_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_subscriptions_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_vpn_key_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_notifications.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_accessibility_new_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/serverinfo/components/models/ServerInfoAction.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.serverinfo.components.models
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.graphics.Color
5 |
6 | data class ServerInfoAction(
7 | val title: String,
8 | val titleColor: Color,
9 | val subtitle: String?,
10 | val trailingComposable: @Composable () -> Unit = {},
11 | val onClick: () -> Unit,
12 | )
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_mic_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_send_rounded.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_person_add_alt_1_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outline_cancel.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_boost.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/message/DiscordMessageEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities.message
2 |
3 | data class DiscordMessageEntity(
4 | val uuid: String,
5 | val channelId: String,
6 | val message: String,
7 | val userId: String,
8 | val replyTo: String,
9 | val replyToMessage: String,
10 | val createdBy: String,
11 | val createdDate: Long,
12 | val modifiedDate: Long,
13 | val metaTitle: String,
14 | val metaDesc: String,
15 | val metaImageUrl: String,
16 | val metaUrl: String
17 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_notification_important_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/local/database/DiscordDatabase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.local.database
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import dev.baseio.discordjetpackcompose.local.dao.DiscordMessageDao
6 | import dev.baseio.discordjetpackcompose.local.model.DBDiscordMessage
7 |
8 | @Database(
9 | entities = [DBDiscordMessage::class],
10 | version = 1,
11 | exportSchema = false
12 | )
13 | abstract class DiscordDatabase : RoomDatabase() {
14 | abstract fun discordMessageDao(): DiscordMessageDao
15 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_account_circle_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_contact_support_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/repositories/ServerRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import dev.baseio.discordjetpackcompose.entities.NetworkState
4 | import dev.baseio.discordjetpackcompose.entities.search.SearchSheetListItemEntity
5 | import dev.baseio.discordjetpackcompose.entities.server.ServerEntity
6 |
7 | interface ServerRepo {
8 | suspend fun getServer(serverId: String): NetworkState
9 | suspend fun getServerList(): NetworkState>
10 | suspend fun getSearchSheetItemList(): NetworkState>
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_account_box_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/models/Country.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.models
2 |
3 |
4 | import androidx.annotation.Keep
5 | import com.google.gson.annotations.SerializedName
6 |
7 | @Keep
8 | data class Country(
9 | @SerializedName("alpha2")
10 | val alpha2: String = "",
11 | @SerializedName("currencyCode")
12 | val currencyCode: String? = null,
13 | @SerializedName("localeForICU")
14 | val localeForICU: String? = null,
15 | @SerializedName("name")
16 | val name: String = "",
17 | @SerializedName("phoneCountryCode")
18 | val phoneCountryCode: String = ""
19 | )
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/repositories/MessagesRepo.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import androidx.paging.PagingData
4 | import dev.baseio.discordjetpackcompose.entities.message.DiscordMessageEntity
5 | import dev.baseio.discordjetpackcompose.entities.message.DiscordUrlMetaEntity
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | interface MessagesRepo {
9 | fun fetchMessages(params: String?): Flow>
10 | suspend fun sendMessage(params: DiscordMessageEntity): DiscordMessageEntity
11 | suspend fun fetchUrlMetadata(url: String?): DiscordUrlMetaEntity?
12 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/di/dispatcher/RealCoroutineDispatcherProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di.dispatcher
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.Dispatchers
5 |
6 | open class RealCoroutineDispatcherProvider : CoroutineDispatcherProvider {
7 | override val main: CoroutineDispatcher by lazy { Dispatchers.Main }
8 | override val io: CoroutineDispatcher by lazy { Dispatchers.IO }
9 | override val default: CoroutineDispatcher by lazy { Dispatchers.Default }
10 | override val unconfirmed: CoroutineDispatcher by lazy { Dispatchers.Unconfined }
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/di/NavigationModule.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import dev.baseio.discordjetpackcompose.navigator.ComposeNavigator
8 | import dev.baseio.discordjetpackcompose.navigator.DiscordComposeNavigator
9 | import javax.inject.Singleton
10 |
11 | @Module
12 | @InstallIn(SingletonComponent::class)
13 | abstract class NavigationModule {
14 |
15 | @Binds
16 | @Singleton
17 | abstract fun provideComposeNavigator(praxisComposeNavigator: DiscordComposeNavigator): ComposeNavigator
18 | }
19 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/chat/FetchUrlMetadataUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases.chat
2 |
3 | import dev.baseio.discordjetpackcompose.entities.message.DiscordUrlMetaEntity
4 | import dev.baseio.discordjetpackcompose.repositories.MessagesRepo
5 | import dev.baseio.discordjetpackcompose.usecases.BaseUseCase
6 | import javax.inject.Inject
7 |
8 | class FetchUrlMetadataUseCase @Inject constructor(private val messagesRepo: MessagesRepo) :
9 | BaseUseCase {
10 | override suspend fun perform(params: String): DiscordUrlMetaEntity? {
11 | return messagesRepo.fetchUrlMetadata(params)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/mappers/CountryMapper.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.mappers
2 |
3 | import dev.baseio.discordjetpackcompose.entities.CountryEntity
4 | import dev.baseio.discordjetpackcompose.models.Country
5 |
6 | fun Country.toDomainEntity() = CountryEntity(
7 | alpha2 = alpha2,
8 | currencyCode = currencyCode,
9 | localeForICU = localeForICU,
10 | name = name,
11 | phoneCountryCode = phoneCountryCode
12 | )
13 |
14 | fun CountryEntity.toDataModel() = Country(
15 | alpha2 = alpha2,
16 | currencyCode = currencyCode,
17 | localeForICU = localeForICU,
18 | name = name,
19 | phoneCountryCode = phoneCountryCode
20 | )
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/chat/SendMessageUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases.chat
2 |
3 | import dev.baseio.discordjetpackcompose.entities.message.DiscordMessageEntity
4 | import dev.baseio.discordjetpackcompose.repositories.MessagesRepo
5 | import dev.baseio.discordjetpackcompose.usecases.BaseUseCase
6 | import javax.inject.Inject
7 |
8 | class SendMessageUseCase @Inject constructor(private val messagesRepo: MessagesRepo) :
9 | BaseUseCase {
10 | override suspend fun perform(params: DiscordMessageEntity): DiscordMessageEntity {
11 | return messagesRepo.sendMessage(params)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.github/issue_template/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/utils/ExtensionUtils.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.utils
2 |
3 | import android.content.Context
4 | import com.google.gson.Gson
5 | import com.google.gson.reflect.TypeToken
6 | import timber.log.Timber
7 |
8 | inline fun Context.readAssetFile(filePath: String): T? {
9 | return try {
10 | val jsonDataFromFile = this.assets.open(filePath).bufferedReader().use { it.readText() }
11 | val returnType = object : TypeToken() {}.type
12 | Gson().fromJson(jsonDataFromFile, returnType)
13 | } catch (ex: Exception) {
14 | Timber.e(ex, "Some error occurred. Reason:")
15 | null
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Minimal Android CI Workflow
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | apk:
10 | name: Generate APK
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v1
15 | - name: Setup JDK
16 | uses: actions/setup-java@v1
17 | with:
18 | java-version: 11
19 | - name: Build APK
20 | run: bash ./gradlew assembleDebug --stacktrace
21 | - name: Upload APK
22 | uses: actions/upload-artifact@v2
23 | with:
24 | name: apk
25 | path: app/build/outputs/apk/debug/app-debug.apk
26 | retention-days: 5
27 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/dev/baseio/discordjetpackcompose/ui/utils/ExtensionUtils.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.utils
2 |
3 | import android.content.Context
4 | import com.google.gson.Gson
5 | import com.google.gson.reflect.TypeToken
6 | import timber.log.Timber
7 |
8 | inline fun Context.readAssetFile(filePath: String): T? {
9 | return try {
10 | val jsonDataFromFile = this.assets.open(filePath).bufferedReader().use { it.readText() }
11 | val returnType = object : TypeToken() {}.type
12 | Gson().fromJson(jsonDataFromFile, returnType)
13 | } catch (ex: Exception) {
14 | Timber.e(ex, "Some error occurred. Reason:")
15 | null
16 | }
17 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/di/DispatcherModule.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import dev.baseio.discordjetpackcompose.di.dispatcher.CoroutineDispatcherProvider
8 | import dev.baseio.discordjetpackcompose.di.dispatcher.RealCoroutineDispatcherProvider
9 | import javax.inject.Singleton
10 |
11 | @InstallIn(SingletonComponent::class)
12 | @Module
13 | class DispatcherModule {
14 | @Provides
15 | @Singleton
16 | fun providesCoroutineDispatcher(): CoroutineDispatcherProvider {
17 | return RealCoroutineDispatcherProvider()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/search/SearchSheetListItemEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities.search
2 |
3 | enum class SearchFilter(val title: String, val sign: Char) {
4 | Users(title = "Users", sign = '@'),
5 | TextChannels(title = "Text Channels", sign = '#'),
6 | VoiceChannels(title = "Voice Channels", sign = '!'),
7 | Servers(title = "Servers", sign = '*'),
8 | }
9 |
10 | data class SearchSheetListItemEntity(
11 | val id: String,
12 | val itemType: SearchFilter,
13 | val iconUri: Any?,
14 | val title: String,
15 | val subtitle: String? = null,
16 | val serverName: String? = null,
17 | val unreadCount: Int? = null,
18 | )
19 |
--------------------------------------------------------------------------------
/navigator/src/main/java/dev/baseio/discordjetpackcompose/navigator/NavigationCommand.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.navigator
2 |
3 | import androidx.navigation.NavOptions
4 |
5 | sealed class NavigationCommand {
6 | object NavigateUp : NavigationCommand()
7 | }
8 |
9 | sealed class ComposeNavigationCommand : NavigationCommand() {
10 | data class NavigateToRoute(val route: String, val options: NavOptions? = null) :
11 | ComposeNavigationCommand()
12 |
13 | data class NavigateUpWithResult(
14 | val key: String,
15 | val result: T,
16 | val route: String? = null
17 | ) : ComposeNavigationCommand()
18 |
19 | data class PopUpToRoute(val route: String, val inclusive: Boolean) : ComposeNavigationCommand()
20 | }
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/chat/FetchMessagesUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases.chat
2 |
3 | import androidx.paging.PagingData
4 | import dev.baseio.discordjetpackcompose.entities.message.DiscordMessageEntity
5 | import dev.baseio.discordjetpackcompose.repositories.MessagesRepo
6 | import dev.baseio.discordjetpackcompose.usecases.BaseUseCase
7 | import kotlinx.coroutines.flow.Flow
8 | import javax.inject.Inject
9 |
10 | class FetchMessagesUseCase @Inject constructor(private val messagesRepo: MessagesRepo) :
11 | BaseUseCase, String> {
12 | override fun performStreaming(params: String?): Flow> {
13 | return messagesRepo.fetchMessages(params)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/repositories/CountryRepoImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import android.content.Context
4 | import dev.baseio.discordjetpackcompose.entities.CountryEntity
5 | import dev.baseio.discordjetpackcompose.mappers.toDomainEntity
6 | import dev.baseio.discordjetpackcompose.models.Country
7 | import dev.baseio.discordjetpackcompose.utils.readAssetFile
8 |
9 | class CountryRepoImpl(private val context: Context) : CountryRepo {
10 | companion object {
11 | const val CountriesAssetFilePath = "countries.json"
12 | }
13 |
14 | override suspend fun fetchCountriesFromAssetFile(): List? =
15 | context.readAssetFile>(CountriesAssetFilePath)?.map { it.toDomainEntity() }
16 | }
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/ChatUserEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities
2 |
3 | import dev.baseio.discordjetpackcompose.utils.Constants
4 |
5 | /**
6 | * Represents a User in Discord
7 | * @param username Uniquely identifies this user
8 | * @param name Name of the user ([username] is used if no name is given)
9 | * @param currentStatus Current status of the user
10 | * @param isOnline Whether the user is online or offline
11 | * @param profileImage Profile image of the user (optional)
12 | * */
13 | data class ChatUserEntity(
14 | val username: String,
15 | val name: String = username,
16 | val currentStatus: String? = null,
17 | val isOnline: Boolean,
18 | val profileImage: String = Constants.MMLogoUrl,
19 | )
20 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/data/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/navigator/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_card_giftcard_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_color_lens_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/usecases/BaseUseCase.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.usecases
2 |
3 | import kotlinx.coroutines.flow.Flow
4 |
5 | interface BaseUseCase {
6 |
7 | /**
8 | * Perform an operation with no input parameters.
9 | * Will throw an exception by default, if not implemented but invoked.
10 | *
11 | * @return
12 | */
13 | suspend fun perform(): Result = throw NotImplementedError()
14 |
15 | /**
16 | * Perform an operation.
17 | * Will throw an exception by default, if not implemented but invoked.
18 | *
19 | * @param params
20 | * @return
21 | */
22 | suspend fun perform(params: ExecutableParam): Result? = throw NotImplementedError()
23 |
24 | fun performStreaming(params: ExecutableParam?): Flow = throw NotImplementedError()
25 | }
--------------------------------------------------------------------------------
/navigator/src/androidTest/java/dev/baseio/slackclone/navigator/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.navigator
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("dev.baseio.discordjetpackcompose.navigator.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/di/DataMappersModule.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import dev.baseio.discordjetpackcompose.entities.message.DiscordMessageEntity
8 | import dev.baseio.discordjetpackcompose.local.model.DBDiscordMessage
9 | import dev.baseio.discordjetpackcompose.mappers.DiscordMessageMapper
10 | import dev.baseio.discordjetpackcompose.mappers.EntityMapper
11 | import javax.inject.Singleton
12 |
13 | @Module
14 | @InstallIn(SingletonComponent::class)
15 | abstract class DataMappersModule {
16 |
17 | @Binds
18 | @Singleton
19 | abstract fun bindDiscordMessageMapper(
20 | discordMessageMapper: DiscordMessageMapper
21 | ): EntityMapper
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/viewmodels/LoginScreenViewModel.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.viewmodels
2 |
3 | import androidx.lifecycle.ViewModel
4 | import dagger.hilt.android.lifecycle.HiltViewModel
5 | import kotlinx.coroutines.flow.MutableStateFlow
6 | import kotlinx.coroutines.flow.StateFlow
7 | import kotlinx.coroutines.flow.asStateFlow
8 | import javax.inject.Inject
9 |
10 | @HiltViewModel
11 | class LoginScreenViewModel @Inject constructor() : ViewModel() {
12 | private val _showDialog = MutableStateFlow(false)
13 | val showDialog: StateFlow = _showDialog.asStateFlow()
14 |
15 | fun onOpenDialogClicked() {
16 | _showDialog.value = true
17 | }
18 |
19 | fun onDialogConfirm() {
20 | _showDialog.value = false
21 | }
22 |
23 | fun onDialogDismiss() {
24 | _showDialog.value = false
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/utils/NetworkUtils.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.utils
2 |
3 | import dev.baseio.discordjetpackcompose.data.BuildConfig
4 | import dev.baseio.discordjetpackcompose.di.dispatcher.CoroutineDispatcherProvider
5 | import dev.baseio.discordjetpackcompose.entities.NetworkState
6 | import kotlinx.coroutines.withContext
7 |
8 | suspend fun safeApiCall(
9 | coroutineDispatcherProvider: CoroutineDispatcherProvider,
10 | debugResponse: T,
11 | releaseResponse: suspend () -> T
12 | ): NetworkState {
13 | return withContext(coroutineDispatcherProvider.io) {
14 | try {
15 | NetworkState.Success(
16 | data = if (BuildConfig.DEBUG) debugResponse
17 | else debugResponse // TODO: Use releaseResponse here
18 | )
19 | } catch (t: Throwable) {
20 | NetworkState.Failure(throwable = t)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/server/ChannelEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities.server
2 |
3 | /**
4 | * A ChannelEntity depicts a single channel inside a server.
5 | * @param id Uniquely identifies the channel
6 | * @param name Channel name that would be publicly displayed
7 | * @param type A channel can be of multiple types as defined inside [ChannelType]
8 | * @param category Defines which category does this channel belong to (there can be multiple channels inside a single category)
9 | * @param unreadCount How many unread messages are there inside this channel
10 | * */
11 | data class ChannelEntity(
12 | val id: String,
13 | val name: String,
14 | val type: ChannelType,
15 | val category: String? = null,
16 | val unreadCount: Int = 0,
17 | val isUnread: Boolean = false,
18 | )
19 |
20 | enum class ChannelType {
21 | PUBLIC, PRIVATE, BROADCAST, PODCAST, CONVERSATION
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_nitro.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.github/issue_template/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_hashtag_solid.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/local/model/DBDiscordMessage.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.local.model
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(tableName = "discordMessage")
8 | data class DBDiscordMessage(
9 | @PrimaryKey val uuid: String,
10 | @ColumnInfo(name = "channelId") val channelId: String,
11 | @ColumnInfo(name = "message") val message: String,
12 | @ColumnInfo(name = "from") val userId: String,
13 | @ColumnInfo(name = "replyTo") val replyTo: String,
14 | @ColumnInfo(name = "replyToMessage") val replyToMessage: String,
15 | @ColumnInfo(name = "createdBy") val createdBy: String,
16 | @ColumnInfo(name = "createdDate") val createdDate: Long,
17 | @ColumnInfo(name = "modifiedDate") val modifiedDate: Long,
18 | @ColumnInfo(name = "metaTitle") val metaTitle: String,
19 | @ColumnInfo(name = "metaDesc") val metaDesc: String,
20 | @ColumnInfo(name = "metaImageUrl") val metaImageUrl: String,
21 | @ColumnInfo(name = "metaUrl") val metaUrl: String
22 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_nitro_verified.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/di/DataModule.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di
2 |
3 | import android.content.Context
4 | import androidx.room.Room
5 | import dagger.Module
6 | import dagger.Provides
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.android.qualifiers.ApplicationContext
9 | import dagger.hilt.components.SingletonComponent
10 | import dev.baseio.discordjetpackcompose.local.dao.DiscordMessageDao
11 | import dev.baseio.discordjetpackcompose.local.database.DiscordDatabase
12 | import javax.inject.Singleton
13 |
14 | @Module
15 | @InstallIn(SingletonComponent::class)
16 | object DataModule {
17 | @Provides
18 | @Singleton
19 | fun provideDatabase(@ApplicationContext context: Context): DiscordDatabase {
20 | return Room.inMemoryDatabaseBuilder(
21 | context,
22 | DiscordDatabase::class.java,
23 | ).fallbackToDestructiveMigration().allowMainThreadQueries().build()
24 | }
25 |
26 | @Provides
27 | @Singleton
28 | fun providesDiscordMessageDao(discordDatabase: DiscordDatabase): DiscordMessageDao =
29 | discordDatabase.discordMessageDao()
30 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/local/dao/DiscordMessageDao.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.local.dao
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.room.Dao
5 | import androidx.room.Insert
6 | import androidx.room.OnConflictStrategy
7 | import androidx.room.Query
8 | import dev.baseio.discordjetpackcompose.local.model.DBDiscordMessage
9 |
10 | @Dao
11 | interface DiscordMessageDao {
12 | @Query("SELECT * FROM discordMessage")
13 | fun getAll(): List
14 |
15 | @Insert(onConflict = OnConflictStrategy.REPLACE)
16 | fun insertAll(messages: List)
17 |
18 | @Insert(onConflict = OnConflictStrategy.REPLACE)
19 | fun insert(message: DBDiscordMessage)
20 |
21 | // The Int type parameter tells Room to use a PositionalDataSource object.
22 | @Query("SELECT * FROM discordMessage where channelId = :params ORDER BY createdDate DESC")
23 | fun messagesByDate(params: String?): PagingSource
24 |
25 | @Query("SELECT * from discordMessage where uuid like :uuid")
26 | fun getById(uuid: String): DBDiscordMessage
27 | }
--------------------------------------------------------------------------------
/team-props/git-hooks.gradle.kts:
--------------------------------------------------------------------------------
1 | fun isLinuxOrMacOs(): Boolean {
2 | val osName = System.getProperty("os.name")
3 | .toLowerCase()
4 | return osName.contains("linux") || osName.contains("mac os") || osName.contains("macos")
5 | }
6 |
7 | tasks.create("copyGitHooks") {
8 | description = "Copies the git hooks from team-props/git-hooks to the .git folder."
9 | from("$rootDir/team-props/git-hooks/") {
10 | include("**/*.sh")
11 | rename("(.*).sh", "$1")
12 | }
13 | into("$rootDir/.git/hooks")
14 | onlyIf { isLinuxOrMacOs() }
15 | }
16 |
17 | tasks.create("installGitHooks") {
18 | description = "Installs the pre-commit git hooks from team-props/git-hooks."
19 | group = "git hooks"
20 | workingDir(rootDir)
21 | commandLine("chmod")
22 | args("-R", "+x", ".git/hooks/")
23 | dependsOn("copyGitHooks")
24 | onlyIf { isLinuxOrMacOs() }
25 | doLast {
26 | logger.info("Git hook installed successfully.")
27 | }
28 | }
29 |
30 | tasks.getByName("installGitHooks")
31 | .dependsOn(getTasksByName("copyGitHooks", true))
32 | tasks.getByPath("app:preBuild")
33 | .dependsOn(getTasksByName("installGitHooks", true))
34 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_language_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_qr_code_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_settings_applications_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
19 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/dark_app_logo.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/light_app_logo.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_discord_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/benchmark/src/main/java/dev/baseio/discordjetpackcompose/baselineprof/BaselineProfileGenerator.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.baselineprof
2 |
3 | import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
4 | import androidx.benchmark.macro.junit4.BaselineProfileRule
5 | import androidx.test.ext.junit.runners.AndroidJUnit4
6 | import androidx.test.uiautomator.By
7 | import org.junit.Rule
8 | import org.junit.Test
9 | import org.junit.runner.RunWith
10 |
11 | /**
12 | * Generates a baseline profile which can be copied to `app/src/main/baseline-prof.txt`.
13 | */
14 | @ExperimentalBaselineProfilesApi
15 | @RunWith(AndroidJUnit4::class)
16 | class BaselineProfileGenerator {
17 |
18 | @get:Rule val baselineProfileRule = BaselineProfileRule()
19 |
20 | @Test
21 | fun startup() =
22 | baselineProfileRule.collectBaselineProfile(
23 | packageName = "dev.baseio.discordjetpackcompose"
24 | ) {
25 | pressHome()
26 | // This block defines the app's critical user journey. Here we are interested in
27 | // optimizing for app startup. But you can also navigate and scroll
28 | // through your most important UI.
29 | startActivityAndWait()
30 |
31 | device.waitForIdle()
32 | device.run {
33 | findObject(By.text("Login"))
34 | .click()
35 | waitForIdle()
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/onboarding/OnboardingRoute.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.onboarding
2 |
3 |
4 | import androidx.navigation.NavGraphBuilder
5 | import androidx.navigation.compose.composable
6 | import androidx.navigation.navigation
7 | import dev.baseio.discordjetpackcompose.navigator.ComposeNavigator
8 | import dev.baseio.discordjetpackcompose.navigator.DiscordRoute
9 | import dev.baseio.discordjetpackcompose.navigator.DiscordScreen
10 | import dev.baseio.discordjetpackcompose.ui.routes.onboarding.screens.login.LoginScreen
11 | import dev.baseio.discordjetpackcompose.ui.routes.onboarding.screens.welcome.WelcomeScreen
12 | import dev.baseio.discordjetpackcompose.ui.routes.onboarding.screens.register.RegisterScreen
13 |
14 | fun NavGraphBuilder.onBoardingRoute(
15 | composeNavigator: ComposeNavigator
16 | ) {
17 | navigation(
18 | startDestination = DiscordScreen.Welcome.name,
19 | route = DiscordRoute.OnBoarding.name
20 | ) {
21 | composable(DiscordScreen.Welcome.name) {
22 | WelcomeScreen(composeNavigator)
23 | }
24 | composable(DiscordScreen.Login.name) {
25 | LoginScreen(composeNavigator)
26 | }
27 | composable(DiscordScreen.Register.name) {
28 | RegisterScreen(composeNavigator)
29 | }
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/createServer/ServerTemplates.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.createServer
2 |
3 | import dev.baseio.discordjetpackcompose.R
4 |
5 | data class SampleServerTemplateModel(
6 | val textProvider: Int,
7 | val iconProvider: Int
8 | )
9 |
10 | object ServerTemplates {
11 | val type = listOf(
12 | SampleServerTemplateModel(
13 | textProvider = R.string.gaming,
14 | iconProvider = R.drawable.dark_app_logo
15 | ),
16 | SampleServerTemplateModel(
17 | textProvider = R.string.school_club,
18 | iconProvider = R.drawable.dark_app_logo
19 | ),
20 | SampleServerTemplateModel(
21 | textProvider = R.string.study_group,
22 | iconProvider = R.drawable.dark_app_logo
23 | ),
24 | SampleServerTemplateModel(
25 | textProvider = R.string.friends,
26 | iconProvider = R.drawable.dark_app_logo
27 | ),
28 | SampleServerTemplateModel(
29 | textProvider = R.string.artists_and_creators,
30 | iconProvider = R.drawable.dark_app_logo
31 | ),
32 | SampleServerTemplateModel(
33 | textProvider = R.string.local_community,
34 | iconProvider = R.drawable.dark_app_logo
35 | )
36 | )
37 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/mappers/DiscordMessageMapper.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.mappers
2 |
3 | import dev.baseio.discordjetpackcompose.entities.message.DiscordMessageEntity
4 | import dev.baseio.discordjetpackcompose.local.model.DBDiscordMessage
5 | import javax.inject.Inject
6 |
7 | class DiscordMessageMapper @Inject constructor() : EntityMapper {
8 |
9 | override fun mapToDomain(entity: DBDiscordMessage): DiscordMessageEntity {
10 | return DiscordMessageEntity(
11 | entity.uuid,
12 | entity.channelId,
13 | entity.message,
14 | entity.userId,
15 | entity.replyTo,
16 | entity.replyToMessage,
17 | entity.createdBy,
18 | entity.createdDate,
19 | entity.modifiedDate,
20 | entity.metaTitle,
21 | entity.metaDesc,
22 | entity.metaImageUrl,
23 | entity.metaUrl
24 | )
25 | }
26 |
27 | override fun mapToData(model: DiscordMessageEntity): DBDiscordMessage {
28 | return DBDiscordMessage(
29 | model.uuid,
30 | model.channelId,
31 | model.message,
32 | model.userId,
33 | model.replyTo,
34 | model.replyToMessage,
35 | model.createdBy,
36 | model.createdDate,
37 | model.modifiedDate,
38 | model.metaTitle,
39 | model.metaDesc,
40 | model.metaImageUrl,
41 | model.metaUrl
42 | )
43 | }
44 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/components/DiscordAppBar.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.components
2 |
3 | import androidx.compose.foundation.layout.RowScope
4 | import androidx.compose.material.AppBarDefaults
5 | import androidx.compose.material.TopAppBar
6 | import androidx.compose.material.contentColorFor
7 | import androidx.compose.material.primarySurface
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.unit.Dp
12 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
13 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordSurface
14 | import dev.baseio.discordjetpackcompose.ui.theme.primarySurface
15 |
16 | @Composable
17 | fun DiscordAppBar(
18 | modifier: Modifier = Modifier,
19 | title: @Composable () -> Unit = {},
20 | navigationIcon: @Composable (() -> Unit)? = null,
21 | actions: @Composable RowScope.() -> Unit = {},
22 | backgroundColor: Color = DiscordColorProvider.colors.primarySurface,
23 | contentColor: Color = contentColorFor(backgroundColor),
24 | elevation: Dp = AppBarDefaults.TopAppBarElevation,
25 | ) {
26 | DiscordSurface(
27 | color = backgroundColor,
28 | contentColor = contentColor,
29 | elevation = elevation
30 | ) {
31 | TopAppBar(
32 | title, modifier, navigationIcon, actions, backgroundColor, contentColor, elevation
33 | )
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/userSettings/components/SettingsTitle.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.components
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.text.font.FontWeight
11 | import androidx.compose.ui.text.style.TextAlign
12 | import androidx.compose.ui.unit.dp
13 | import androidx.compose.ui.unit.sp
14 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
15 | import dev.baseio.discordjetpackcompose.ui.theme.Typography
16 | import dev.baseio.discordjetpackcompose.ui.theme.user_settings_text
17 |
18 | @Composable
19 | fun GetSettingsSubtitle(title: String) {
20 | Box(
21 | modifier = Modifier
22 | .background(DiscordColorProvider.colors.settingsBackground)
23 | .fillMaxWidth()
24 | ) {
25 | Text(
26 | text = title,
27 | style = Typography.h3.copy(
28 | fontWeight = FontWeight.SemiBold,
29 | fontSize = 15.sp,
30 | color = user_settings_text
31 | ),
32 | textAlign = TextAlign.Start,
33 | modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 16.dp)
34 | )
35 | }
36 | }
--------------------------------------------------------------------------------
/navigator/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id(BuildPlugins.ANDROID_LIBRARY_PLUGIN)
3 | id(BuildPlugins.KOTLIN_ANDROID_PLUGIN)
4 | id(BuildPlugins.KOTLIN_KAPT)
5 | }
6 |
7 | android {
8 | compileSdk = ProjectProperties.COMPILE_SDK
9 |
10 | defaultConfig {
11 | minSdk = (ProjectProperties.MIN_SDK)
12 | targetSdk = (ProjectProperties.TARGET_SDK)
13 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
14 | }
15 |
16 | buildTypes {
17 | getByName("release") {
18 | isMinifyEnabled = false
19 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
20 | }
21 | }
22 |
23 | compileOptions {
24 | sourceCompatibility = JavaVersion.VERSION_1_8
25 | targetCompatibility = JavaVersion.VERSION_1_8
26 | }
27 |
28 | kotlinOptions {
29 | jvmTarget = "1.8"
30 | }
31 |
32 | composeOptions {
33 | kotlinCompilerExtensionVersion = Lib.Android.COMPOSE_COMPILER_VERSION
34 | }
35 | }
36 |
37 | // Required for annotation processing plugins like Dagger
38 | kapt {
39 | generateStubs = true
40 | correctErrorTypes = true
41 | }
42 |
43 | dependencies {
44 | /*Kotlin*/
45 | implementation(Lib.Android.APP_COMPAT)
46 | implementation(Lib.Kotlin.KTX_CORE)
47 | api(Lib.Async.COROUTINES)
48 | api(Lib.Async.COROUTINES_ANDROID)
49 |
50 | implementation(Lib.Kotlin.KT_STD)
51 | implementation(Lib.Android.COMPOSE_NAVIGATION)
52 |
53 | implementation(Lib.Android.COMPOSE_NAVIGATION)
54 | implementation(Lib.Di.hiltNavigationCompose)
55 | }
--------------------------------------------------------------------------------
/benchmark/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.test")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | android {
7 | compileSdk = 32
8 |
9 | compileOptions {
10 | sourceCompatibility = JavaVersion.VERSION_1_8
11 | targetCompatibility = JavaVersion.VERSION_1_8
12 | }
13 |
14 | kotlinOptions {
15 | jvmTarget = "1.8"
16 | }
17 |
18 | defaultConfig {
19 | minSdk = 28
20 | targetSdk = 32
21 |
22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
23 | }
24 |
25 | buildTypes {
26 | // This benchmark buildType is used for benchmarking, and should function like your
27 | // release build (for example, with minification on). It"s signed with a debug key
28 | // for easy local/CI testing.
29 | create("benchmark") {
30 | isDebuggable = true
31 | signingConfig = getByName("debug").signingConfig
32 | matchingFallbacks += listOf("release")
33 | }
34 | }
35 |
36 | targetProjectPath = ":app"
37 | experimentalProperties["android.experimental.self-instrumenting"] = true
38 | }
39 |
40 | dependencies {
41 | implementation("androidx.test.ext:junit:1.1.3")
42 | implementation("androidx.test.espresso:espresso-core:3.4.0")
43 | implementation("androidx.test.uiautomator:uiautomator:2.2.0")
44 | implementation("androidx.benchmark:benchmark-macro-junit4:1.1.0-beta04")
45 | implementation("androidx.profileinstaller:profileinstaller:1.2.0-rc01")
46 | }
47 |
48 | androidComponents {
49 | beforeVariants(selector().all()) {
50 | it.enabled = it.buildType == "benchmark"
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/notifications/components/SectionItem.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.components
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.fillMaxWidth
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.material.ContentAlpha
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Alignment
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.draw.alpha
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.unit.Dp
16 | import androidx.compose.ui.unit.dp
17 |
18 | @Composable
19 | fun SectionItem(
20 | color: Color? = null,
21 | disabled: Boolean = false,
22 | paddingVertical: Dp = 0.dp,
23 | paddingHorizontal: Dp = 16.dp,
24 | onClick: () -> Unit,
25 | leadingComposable: @Composable () -> Unit = {},
26 | trailingComposable: @Composable () -> Unit = {},
27 | ) {
28 | Row(
29 | modifier = Modifier
30 | .fillMaxWidth()
31 | .background(color = color ?: Color.Transparent)
32 | .alpha(if(disabled) ContentAlpha.disabled else 1.0f)
33 | .clickable(onClick = onClick)
34 | .padding(horizontal = paddingHorizontal, vertical = paddingVertical),
35 | horizontalArrangement = Arrangement.SpaceBetween,
36 | verticalAlignment = Alignment.CenterVertically
37 | ) {
38 | leadingComposable()
39 | trailingComposable()
40 | }
41 | }
--------------------------------------------------------------------------------
/data/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id(BuildPlugins.ANDROID_LIBRARY_PLUGIN)
3 | id(BuildPlugins.KOTLIN_ANDROID_PLUGIN)
4 | id(BuildPlugins.KOTLIN_KAPT)
5 | id(BuildPlugins.DAGGER_HILT)
6 | }
7 |
8 | android {
9 | compileSdk = ProjectProperties.COMPILE_SDK
10 |
11 | defaultConfig {
12 | minSdk = (ProjectProperties.MIN_SDK)
13 | targetSdk = (ProjectProperties.TARGET_SDK)
14 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | buildTypes {
18 | getByName("release") {
19 | isMinifyEnabled = false
20 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
21 | }
22 | }
23 | }
24 |
25 | // Required for annotation processing plugins like Dagger
26 | kapt {
27 | generateStubs = true
28 | correctErrorTypes = true
29 | }
30 |
31 | dependencies {
32 |
33 | implementation(project(":domain"))
34 |
35 | api(Lib.Android.MATERIAL_EXTENDED_ICONS) // todo: DATA shouldn't deal with presentation deps
36 |
37 | /*Kotlin*/
38 | api(Lib.Kotlin.KT_STD)
39 | api(Lib.Async.COROUTINES)
40 |
41 | /* Paging */
42 | implementation(Lib.Paging.PAGING_3)
43 | implementation(Lib.Jsoup.JSOUP)
44 |
45 | /* Room */
46 | api(Lib.Room.roomRuntime)
47 | kapt(Lib.Room.roomCompiler)
48 | api(Lib.Room.roomKtx)
49 | api(Lib.Room.roomPaging)
50 |
51 | /* Networking */
52 | api(Lib.Networking.RETROFIT)
53 | api(Lib.Networking.RETROFIT_GSON)
54 | api(Lib.Networking.LOGGING)
55 |
56 | api(Lib.Serialization.GSON)
57 |
58 | /* Dependency Injection */
59 | api(Lib.Di.hiltAndroid)
60 | kapt(Lib.Di.hiltAndroidCompiler)
61 |
62 | /* Logger */
63 | implementation(Lib.Logger.TIMBER)
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/viewmodels/RegistrationViewModel.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.viewmodels
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import androidx.compose.runtime.snapshotFlow
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.viewModelScope
9 | import dagger.hilt.android.lifecycle.HiltViewModel
10 | import dev.baseio.discordjetpackcompose.entities.CountryEntity
11 | import dev.baseio.discordjetpackcompose.usecases.FetchCountriesUseCase
12 | import javax.inject.Inject
13 | import kotlinx.coroutines.flow.launchIn
14 | import kotlinx.coroutines.flow.onEach
15 | import kotlinx.coroutines.launch
16 |
17 | @HiltViewModel
18 | class RegistrationViewModel @Inject constructor(
19 | val fetchCountriesUseCase: FetchCountriesUseCase
20 | ) : ViewModel() {
21 | private var countryList: List? by mutableStateOf(null)
22 | var filteredCountryList: List? by mutableStateOf(null)
23 | private set
24 | private var countrySearchQuery: String by mutableStateOf("")
25 |
26 | init {
27 | getCountryList()
28 | snapshotFlow { countrySearchQuery }.onEach { query ->
29 | filteredCountryList = countryList?.filter { country ->
30 | country.name.contains(other = query, ignoreCase = true)
31 | }
32 | }.launchIn(viewModelScope)
33 | }
34 |
35 | private fun getCountryList() {
36 | viewModelScope.launch {
37 | countryList = fetchCountriesUseCase()
38 | }
39 | }
40 |
41 | fun updateCountryQuery(query: String) {
42 | countrySearchQuery = query
43 | }
44 | }
--------------------------------------------------------------------------------
/navigator/src/main/java/dev/baseio/discordjetpackcompose/navigator/Screens.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.navigator
2 |
3 | import androidx.navigation.NamedNavArgument
4 |
5 | sealed class DiscordScreen(
6 | val route: String,
7 | val navArguments: List = emptyList()
8 | ) {
9 | val name: String = route.appendArguments(navArguments)
10 |
11 | object Welcome : DiscordScreen("welcome")
12 | object Register : DiscordScreen("register")
13 | object Login : DiscordScreen("login")
14 | object Dashboard : DiscordScreen("dashboard")
15 | object Friends : DiscordScreen("friends")
16 | object CreateServer : DiscordScreen("createServer")
17 | object NotificationSettings : DiscordScreen("notificationSettings")
18 | object Invite : DiscordScreen("invite")
19 | object UserSettings: DiscordScreen("userSettings")
20 | object Home : DiscordScreen("home")
21 | object Search : DiscordScreen("search")
22 | }
23 |
24 | sealed class DiscordRoute(val name: String) {
25 | object OnBoarding : DiscordRoute("onboardingRoute")
26 | object Dashboard : DiscordRoute("dashboardRoute")
27 | object DashboardBottomNav: DiscordRoute("dashboardBottomNav")
28 | }
29 |
30 | private fun String.appendArguments(navArguments: List): String {
31 | val mandatoryArguments = navArguments.filter { it.argument.defaultValue == null }
32 | .takeIf { it.isNotEmpty() }
33 | ?.joinToString(separator = "/", prefix = "/") { "{${it.name}}" }
34 | .orEmpty()
35 | val optionalArguments = navArguments.filter { it.argument.defaultValue != null }
36 | .takeIf { it.isNotEmpty() }
37 | ?.joinToString(separator = "&", prefix = "?") { "${it.name}={${it.name}}" }
38 | .orEmpty()
39 | return "$this$mandatoryArguments$optionalArguments"
40 | }
--------------------------------------------------------------------------------
/.github/issue_template/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: PULL_REQUEST_TEMPLATE.md
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | # Description
11 |
12 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
13 |
14 | ## Associated Tickets
15 | - [Feature Name](https://github.com/Anmol92verma/DiscordJetpackCompose/issues/{ticket-number})
16 |
17 |
18 | ## Type of change
19 |
20 | Please delete options that are not relevant.
21 |
22 | - [ ] Bug fix (non-breaking change which fixes an issue)
23 | - [ ] New feature (non-breaking change which adds functionality)
24 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
25 | - [ ] This change requires a documentation update
26 |
27 | # How Has This Been Tested?
28 |
29 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
30 |
31 | - [ ] Test A
32 | - [ ] Test B
33 |
34 | **Test Configuration**:
35 | * Firmware version:
36 | * Hardware:
37 | * Toolchain:
38 | * SDK:
39 |
40 | # Checklist:
41 |
42 | - [ ] My code follows the style guidelines of this project
43 | - [ ] I have performed a self-review of my own code
44 | - [ ] I have commented my code, particularly in hard-to-understand areas
45 | - [ ] I have made corresponding changes to the documentation
46 | - [ ] My changes generate no new warnings
47 | - [ ] I have added tests that prove my fix is effective or that my feature works
48 | - [ ] New and existing unit tests pass locally with my changes
49 | - [ ] Any dependent changes have been merged and published in downstream modules
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/di/UseCaseModule.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di
2 |
3 | import dagger.Module
4 | import dagger.Provides
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import dev.baseio.discordjetpackcompose.repositories.CountryRepo
8 | import dev.baseio.discordjetpackcompose.repositories.FriendsRepo
9 | import dev.baseio.discordjetpackcompose.repositories.MessagesRepo
10 | import dev.baseio.discordjetpackcompose.repositories.ServerRepo
11 | import dev.baseio.discordjetpackcompose.usecases.FetchCountriesUseCase
12 | import dev.baseio.discordjetpackcompose.usecases.FetchFriendSuggestionsUseCase
13 | import dev.baseio.discordjetpackcompose.usecases.FetchFriendsUseCase
14 | import dev.baseio.discordjetpackcompose.usecases.GetServerUseCase
15 | import dev.baseio.discordjetpackcompose.usecases.chat.FetchMessagesUseCase
16 | import dev.baseio.discordjetpackcompose.usecases.chat.SendMessageUseCase
17 | import javax.inject.Singleton
18 |
19 | @Module
20 | @InstallIn(SingletonComponent::class)
21 | object UseCaseModule {
22 | @Provides
23 | @Singleton
24 | fun provideFetchCountriesUseCase(countryRepo: CountryRepo): FetchCountriesUseCase =
25 | FetchCountriesUseCase(countryRepo)
26 |
27 | @Provides
28 | @Singleton
29 | fun provideGetServerUseCase(serverRepo: ServerRepo) = GetServerUseCase(serverRepo = serverRepo)
30 |
31 | @Provides
32 | @Singleton
33 | fun provideFriendsSuggestionUseCase(friendsRepo: FriendsRepo) = FetchFriendSuggestionsUseCase(friendsRepo)
34 |
35 | @Provides
36 | @Singleton
37 | fun provideFriendsUseCase(friendsRepo: FriendsRepo) = FetchFriendsUseCase(friendsRepo)
38 |
39 | @Provides
40 | fun provideFetchMessageUseCase(messagesRepo: MessagesRepo) = FetchMessagesUseCase(messagesRepo)
41 |
42 | @Provides
43 | fun provideSendMessageUseCase(messagesRepo: MessagesRepo) = SendMessageUseCase(messagesRepo)
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/viewmodels/FriendsViewModel.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.viewmodels
2 |
3 | import androidx.compose.runtime.MutableState
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.mutableStateOf
6 | import androidx.compose.runtime.setValue
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.viewModelScope
9 | import dagger.hilt.android.lifecycle.HiltViewModel
10 | import dev.baseio.discordjetpackcompose.entities.ChatUserEntity
11 | import dev.baseio.discordjetpackcompose.usecases.FetchCountriesUseCase
12 | import dev.baseio.discordjetpackcompose.usecases.FetchFriendSuggestionsUseCase
13 | import dev.baseio.discordjetpackcompose.usecases.FetchFriendsUseCase
14 | import kotlinx.coroutines.flow.Flow
15 | import kotlinx.coroutines.flow.MutableStateFlow
16 | import kotlinx.coroutines.flow.asStateFlow
17 | import kotlinx.coroutines.flow.emptyFlow
18 | import kotlinx.coroutines.launch
19 | import javax.inject.Inject
20 |
21 | @HiltViewModel
22 | class FriendsViewModel @Inject constructor(
23 | private val fetchFriendsUseCase: FetchFriendsUseCase,
24 | private val fetchFriendSuggestionsUseCase: FetchFriendSuggestionsUseCase,
25 | ) : ViewModel() {
26 |
27 | private val _friendsSuggestionsList = MutableStateFlow>>(emptyFlow())
28 | val friendsSuggestionsList = _friendsSuggestionsList.asStateFlow()
29 |
30 | private val _friendsList = MutableStateFlow>>(emptyFlow())
31 | val friendsList = _friendsList.asStateFlow()
32 |
33 | init {
34 | fetchFriendsList()
35 | fetchSuggestionsList()
36 | }
37 |
38 | private fun fetchSuggestionsList(){
39 | viewModelScope.launch {
40 | _friendsSuggestionsList.value = fetchFriendSuggestionsUseCase()
41 | }
42 | }
43 |
44 | private fun fetchFriendsList(){
45 | viewModelScope.launch {
46 | _friendsList.value = fetchFriendsUseCase()
47 | }
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/navigator/src/main/java/dev/baseio/discordjetpackcompose/navigator/ComposeNavigator.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.navigator
2 |
3 | import androidx.navigation.NavOptionsBuilder
4 | import androidx.navigation.navOptions
5 | import kotlinx.coroutines.flow.*
6 | import javax.inject.Inject
7 |
8 | class DiscordComposeNavigator @Inject constructor(): ComposeNavigator() {
9 |
10 | override fun navigate(route: String, optionsBuilder: (NavOptionsBuilder.() -> Unit)?) {
11 | val options = optionsBuilder?.let { navOptions(it) }
12 | navigationCommands.tryEmit(ComposeNavigationCommand.NavigateToRoute(route, options))
13 | }
14 |
15 | override fun navigateAndClearBackStack(route: String) {
16 | navigationCommands.tryEmit(ComposeNavigationCommand.NavigateToRoute(route, navOptions {
17 | popUpTo(0)
18 | }))
19 | }
20 |
21 | override fun popUpTo(route: String, inclusive: Boolean) {
22 | navigationCommands.tryEmit(ComposeNavigationCommand.PopUpToRoute(route, inclusive))
23 | }
24 |
25 | override fun navigateBackWithResult(
26 | key: String,
27 | result: T,
28 | route: String?
29 | ) {
30 | navigationCommands.tryEmit(
31 | ComposeNavigationCommand.NavigateUpWithResult(
32 | key = key,
33 | result = result,
34 | route = route
35 | )
36 | )
37 | }
38 |
39 | override fun observeResult(key: String, route: String?): Flow {
40 | return navControllerFlow
41 | .filterNotNull()
42 | .flatMapLatest { navController ->
43 | val backStackEntry = route?.let { navController.getBackStackEntry(it) }
44 | ?: navController.currentBackStackEntry
45 |
46 | backStackEntry?.savedStateHandle?.let { savedStateHandle ->
47 | savedStateHandle.getLiveData(key)
48 | .asFlow()
49 | .filter { it != null }
50 | .onEach {
51 | // Nullify the result to avoid resubmitting it
52 | savedStateHandle.set(key, null)
53 | }
54 | } ?: emptyFlow()
55 | }
56 | }
57 |
58 |
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/notifications/components/SubtitleAppBar.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.material.Icon
6 | import androidx.compose.material.IconButton
7 | import androidx.compose.material.Text
8 | import androidx.compose.material.icons.Icons
9 | import androidx.compose.material.icons.filled.ArrowBack
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.text.TextStyle
13 | import androidx.compose.ui.text.font.FontWeight
14 | import androidx.compose.ui.unit.dp
15 | import dev.baseio.discordjetpackcompose.navigator.ComposeNavigator
16 | import dev.baseio.discordjetpackcompose.ui.components.DiscordAppBar
17 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
18 | import dev.baseio.discordjetpackcompose.ui.theme.Typography
19 |
20 | @Composable
21 | fun SubtitledAppBar(
22 | composeNavigator: ComposeNavigator,
23 | title: String,
24 | subtitle: String?
25 | ) {
26 | DiscordAppBar(
27 | navigationIcon = {
28 | IconButton(onClick = {
29 | composeNavigator.navigateUp()
30 | }) {
31 | Icon(
32 | imageVector = Icons.Filled.ArrowBack,
33 | contentDescription = null,
34 | modifier = Modifier.padding(start = 8.dp),
35 | )
36 | }
37 | },
38 | backgroundColor = DiscordColorProvider.colors.discordBackgroundOne.copy(alpha = 0.5f),
39 | elevation = 0.dp,
40 | title = {
41 | Column {
42 | Text(
43 | text = title,
44 | style = TextStyle(fontWeight = FontWeight.Bold, fontSize = Typography.h6.fontSize)
45 | )
46 | if (!subtitle.isNullOrBlank()) {
47 | Text(
48 | text = subtitle,
49 | style = TextStyle(fontSize = Typography.body2.fontSize)
50 | )
51 | }
52 | }
53 | }
54 | )
55 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_emoji_2.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
16 |
21 |
26 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_emoji_3.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
16 |
21 |
26 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/main/dasboard/DashboardUtil.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.main.dasboard
2 |
3 | import androidx.navigation.NavGraph.Companion.findStartDestination
4 | import androidx.navigation.NavHostController
5 | import dev.baseio.discordjetpackcompose.entities.ChatUserEntity
6 | import dev.baseio.discordjetpackcompose.entities.server.ServerEntity
7 | import dev.baseio.discordjetpackcompose.navigator.DiscordScreen
8 | import dev.baseio.discordjetpackcompose.utils.getSampleServer
9 |
10 | fun getFakeChatUserList(): MutableList {
11 | return mutableListOf().apply {
12 | add(
13 | ChatUserEntity(
14 | username = "MEE6",
15 | name = "MEE6",
16 | isOnline = false,
17 | )
18 | )
19 | repeat(20) {
20 | add(
21 | if (it % 2 == 0) {
22 | ChatUserEntity(
23 | username = "TestUser$it",
24 | name = "Test User $it",
25 | currentStatus = "Studying",
26 | isOnline = false,
27 | )
28 | } else {
29 | ChatUserEntity(
30 | username = "TestUser$it",
31 | name = "Test User $it",
32 | isOnline = true,
33 | )
34 | }
35 | )
36 | }
37 | }
38 | }
39 |
40 | fun getFakeServerList(): List {
41 | return listOf(
42 | getSampleServer(serverId = "1"),
43 | getSampleServer(serverId = "2"),
44 | )
45 | }
46 |
47 | fun NavHostController.navigateTab(screen: DiscordScreen) {
48 | navigate(screen.route) {
49 | // Pop up to the start destination of the graph to
50 | // avoid building up a large stack of destinations
51 | // on the back stack as users select items
52 | popUpTo(graph.findStartDestination().id) {
53 | saveState = true
54 | }
55 | // Avoid multiple copies of the same destination when
56 | // reselecting the same item
57 | launchSingleTop = true
58 | // Restore state when reselecting a previously selected item
59 | restoreState = true
60 | }
61 | }
--------------------------------------------------------------------------------
/domain/src/main/java/dev/baseio/discordjetpackcompose/entities/server/ServerEntity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.entities.server
2 |
3 | import dev.baseio.discordjetpackcompose.utils.Constants
4 |
5 | /**
6 | * Model class depicting a Discord server.
7 | * @param id Unique identifier for the server
8 | * @param name Name of the server
9 | * @param thumbnailUri URL to the thumbnail image for the server (Optional)
10 | * @param selectedAnimationUri URL of the animation which will be played whenever this server is selected in the list (Optional)
11 | * @param posterUri URL of the large poster/image to be displayed when this server is selected (Optional)
12 | * @param channels List of channels ([ChannelEntity]) this server contains
13 | * @param allChannelsUnreadCount Total unread message count of all the channels (Optional)
14 | * @param hasNitroSubscription Whether the channel has purchased the Discord Nitro subscription (this will lead to a special icon being displayed before the channel name)
15 | * @param totalMembersCount Displays how many members have joined this server so far
16 | * @param onlineMembersCount Displays how many members are online right now
17 | * @param description A short description about the server (Optional)
18 | * @param boostCount Displays how many Discord Boosts does this channel have
19 | * @param serverEmojiUris List of URIs for emojis which are specific to this server by default (Can be used in other servers too with the Discord Nitro Subscription)
20 | * */
21 | data class ServerEntity(
22 | val id: String,
23 | val name: String,
24 | val thumbnailUri: String = Constants.MMLogoUrl,
25 | val selectedAnimationUri: String? = null,
26 | val posterUri: String? = null,
27 | val channels: List = emptyList(),
28 | val allChannelsUnreadCount: Int = channels.sumOf { it.unreadCount },
29 | val hasNitroSubscription: Boolean,
30 | val totalMembersCount: Int = 0,
31 | val onlineMembersCount: Int = 0,
32 | val description: String? = null,
33 | val boostCount: Int = 0,
34 | val serverEmojiUris: List = emptyList()
35 | )
36 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.runtime.LaunchedEffect
7 | import androidx.core.view.WindowCompat
8 | import androidx.navigation.compose.NavHost
9 | import androidx.navigation.compose.rememberNavController
10 | import com.google.accompanist.insets.ProvideWindowInsets
11 | import dagger.hilt.android.AndroidEntryPoint
12 | import dev.baseio.discordjetpackcompose.navigator.ComposeNavigator
13 | import dev.baseio.discordjetpackcompose.navigator.DiscordRoute
14 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.dashboardRoute
15 | import dev.baseio.discordjetpackcompose.ui.routes.onboarding.onBoardingRoute
16 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordJetpackComposeTheme
17 | import javax.inject.Inject
18 |
19 | @AndroidEntryPoint
20 | class MainActivity : ComponentActivity() {
21 |
22 | @Inject
23 | lateinit var composeNavigator: ComposeNavigator
24 |
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | setTheme(R.style.Theme_DiscordJetpackCompose)
27 |
28 | super.onCreate(savedInstanceState)
29 |
30 | WindowCompat.setDecorFitsSystemWindows(window, false)
31 |
32 | setContent {
33 | val navController = rememberNavController()
34 |
35 | LaunchedEffect(Unit) {
36 | composeNavigator.handleNavigationCommands(navController)
37 | }
38 | DiscordJetpackComposeTheme {
39 |
40 | ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
41 | NavHost(
42 | navController = navController,
43 | startDestination = DiscordRoute.OnBoarding.name,
44 | ) {
45 | onBoardingRoute(composeNavigator)
46 | dashboardRoute(composeNavigator)
47 | }
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/repositories/FriendsRepoImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import dev.baseio.discordjetpackcompose.entities.ChatUserEntity
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.flowOf
6 |
7 | class FriendsRepoImpl() : FriendsRepo {
8 | override suspend fun fetchFriendSuggestions(): Flow> {
9 | return flowOf(
10 | listOf(
11 | ChatUserEntity("Eren yeager", name = "Eren", isOnline = false),
12 | ChatUserEntity("Kamado Tanjiro", name = "Monjiro", isOnline = false)
13 | )
14 | )
15 | }
16 |
17 | override suspend fun fetchFriends(): Flow> {
18 | return flowOf(
19 | listOf(
20 | ChatUserEntity("Mikasa Ackerman", name = "Mikasa", isOnline = false, currentStatus = "Offline"),
21 | ChatUserEntity("Gojo Satoru", name = "Nanami", isOnline = true, currentStatus = "Online"),
22 | ChatUserEntity("Akagami Shanks", name = "Shanks", isOnline = false, currentStatus = "Offline"),
23 | ChatUserEntity("Roronoa Zoro", name = "Zoro", isOnline = false, currentStatus = "Offline"),
24 | ChatUserEntity("Itachi Uchiha", name = "Itachi", isOnline = true, currentStatus = "Online"),
25 | ChatUserEntity("Monkey D Luffy", name = "Luffy", isOnline = true, currentStatus = "Online"),
26 | ChatUserEntity("Hatake Kakashi", name = "Kakashi", isOnline = false, currentStatus = "Offline"),
27 | ChatUserEntity("Gol D Roger", name = "Roger", isOnline = false, currentStatus = "Offline"),
28 | ChatUserEntity("Portgas D Ace", name = "Ace", isOnline = false, currentStatus = "Offline"),
29 | ChatUserEntity("Nico Robin", name = "Robin", isOnline = false, currentStatus = "Offline"),
30 | ChatUserEntity("Anya Forger", name = "Anya", isOnline = false, currentStatus = "Offline"),
31 | ChatUserEntity("Boa Hancock", name = "Boa", isOnline = false, currentStatus = "Offline"),
32 | )
33 | )
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/components/DiscordScaffold.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.components
2 |
3 | import androidx.compose.foundation.layout.PaddingValues
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.material.*
6 | import androidx.compose.material.icons.Icons.Filled
7 | import androidx.compose.material.icons.filled.ArrowBack
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.unit.dp
12 | import com.google.accompanist.insets.statusBarsPadding
13 | import dev.baseio.discordjetpackcompose.navigator.ComposeNavigator
14 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
15 | import dev.baseio.discordjetpackcompose.ui.theme.contentColorFor
16 |
17 | @Composable
18 | fun DiscordScaffold(
19 | modifier: Modifier = Modifier,
20 | scaffoldState: ScaffoldState,
21 | navigator: ComposeNavigator? = null,
22 | backgroundColor: Color = DiscordColorProvider.colors.background,
23 | contentColor: Color = DiscordColorProvider.colors.contentColorFor(DiscordColorProvider.colors.background),
24 | topAppBar: @Composable () -> Unit = {
25 | DiscordAppBar(
26 | navigationIcon = {
27 | IconButton(onClick = {
28 | navigator?.navigateUp()
29 | }) {
30 | Icon(
31 | imageVector = Filled.ArrowBack,
32 | contentDescription = "Back",
33 | modifier = Modifier.padding(start = 8.dp),
34 | )
35 | }
36 | },
37 | backgroundColor = Color.Transparent,
38 | elevation = 0.dp
39 | )
40 | },
41 | content: @Composable (PaddingValues) -> Unit
42 | ) {
43 | Scaffold(
44 | backgroundColor = backgroundColor,
45 | contentColor = contentColor,
46 | modifier = modifier,
47 | scaffoldState = scaffoldState,
48 | topBar = topAppBar,
49 | snackbarHost = {
50 | scaffoldState.snackbarHostState
51 | },
52 | content = content
53 | )
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/userSettings/components/SettingsAppBar.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.components
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.material.DropdownMenu
6 | import androidx.compose.material.DropdownMenuItem
7 | import androidx.compose.material.Icon
8 | import androidx.compose.material.IconButton
9 | import androidx.compose.material.Text
10 | import androidx.compose.material.icons.Icons
11 | import androidx.compose.material.icons.Icons.Filled
12 | import androidx.compose.material.icons.filled.Logout
13 | import androidx.compose.material.icons.filled.MoreVert
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.runtime.getValue
16 | import androidx.compose.runtime.mutableStateOf
17 | import androidx.compose.runtime.remember
18 | import androidx.compose.runtime.setValue
19 | import androidx.compose.ui.Modifier
20 | import androidx.compose.ui.graphics.Color
21 | import androidx.compose.ui.res.stringResource
22 | import androidx.compose.ui.text.style.TextAlign
23 | import androidx.compose.ui.unit.dp
24 | import dev.baseio.discordjetpackcompose.R.string
25 | import dev.baseio.discordjetpackcompose.navigator.ComposeNavigator
26 | import dev.baseio.discordjetpackcompose.navigator.DiscordScreen.Login
27 |
28 | @Composable
29 | fun UserSettingsAppBar(composeNavigator: ComposeNavigator) {
30 | var displayMenu by remember { mutableStateOf(false) }
31 | IconButton(
32 | onClick = { composeNavigator.navigateAndClearBackStack(Login.name) }) {
33 | Icon(
34 | imageVector = Filled.Logout,
35 | contentDescription = stringResource(string.logout),
36 | modifier = Modifier.padding(start = 8.dp),
37 | )
38 | }
39 |
40 | IconButton(onClick = { displayMenu = !displayMenu }) {
41 | Icon(Icons.Default.MoreVert, null)
42 | }
43 | DropdownMenu(
44 | expanded = displayMenu,
45 | onDismissRequest = { displayMenu = false },
46 | modifier = Modifier.background(Color.White)
47 | ) {
48 | DropdownMenuItem(onClick = { }) {
49 | Text(
50 | text = stringResource(string.debug), color = Color.Black,
51 | textAlign = TextAlign.Center
52 | )
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/onboarding/commonui/CenteredTitleSubtitle.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.onboarding.commonui
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Alignment
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.res.stringResource
12 | import androidx.compose.ui.text.font.FontWeight
13 | import androidx.compose.ui.text.style.TextAlign
14 | import androidx.compose.ui.unit.TextUnit
15 | import androidx.compose.ui.unit.dp
16 | import androidx.compose.ui.unit.sp
17 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
18 | import dev.baseio.discordjetpackcompose.ui.theme.Typography
19 |
20 | @Composable
21 | fun CenteredTitleSubtitle(
22 | modifier: Modifier = Modifier,
23 | title: Int,
24 | subtitle: Int,
25 | titleFontSize: TextUnit = 24.sp,
26 | subtitleFontSize: TextUnit = 16.sp
27 | ) {
28 | Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
29 | Title(title = title, titleFontSize = titleFontSize)
30 | Spacer(modifier = Modifier.height(8.dp))
31 | SubTitle(subtitle = subtitle, subtitleFontSize = subtitleFontSize)
32 | }
33 | }
34 |
35 | @Composable
36 | private fun SubTitle(
37 | subtitle: Int,
38 | subtitleFontSize: TextUnit = 16.sp
39 | ) {
40 | Text(
41 | text = stringResource(id = subtitle),
42 | style = Typography.subtitle1.copy(fontSize = subtitleFontSize),
43 | textAlign = TextAlign.Center,
44 | color = DiscordColorProvider.colors.onSurface
45 | )
46 | }
47 |
48 | @Composable
49 | private fun Title(
50 | title: Int,
51 | titleFontSize: TextUnit = 24.sp
52 | ) {
53 | Text(
54 | text = stringResource(id = title),
55 | style = Typography.h5.copy(
56 | fontWeight = FontWeight.Bold,
57 | fontSize = titleFontSize
58 | ),
59 | textAlign = TextAlign.Center,
60 | color = DiscordColorProvider.colors.onSurface
61 | )
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_emoji_5.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
16 |
21 |
26 |
31 |
36 |
37 |
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/di/RepositoryModule.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.di
2 |
3 | import android.content.Context
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.android.qualifiers.ApplicationContext
8 | import dagger.hilt.components.SingletonComponent
9 | import dev.baseio.discordjetpackcompose.di.dispatcher.CoroutineDispatcherProvider
10 | import dev.baseio.discordjetpackcompose.entities.message.DiscordMessageEntity
11 | import dev.baseio.discordjetpackcompose.local.dao.DiscordMessageDao
12 | import dev.baseio.discordjetpackcompose.local.model.DBDiscordMessage
13 | import dev.baseio.discordjetpackcompose.mappers.EntityMapper
14 | import dev.baseio.discordjetpackcompose.repositories.CountryRepo
15 | import dev.baseio.discordjetpackcompose.repositories.CountryRepoImpl
16 | import dev.baseio.discordjetpackcompose.repositories.FriendsRepo
17 | import dev.baseio.discordjetpackcompose.repositories.FriendsRepoImpl
18 | import dev.baseio.discordjetpackcompose.repositories.MessagesRepo
19 | import dev.baseio.discordjetpackcompose.repositories.MessagesRepoImpl
20 | import dev.baseio.discordjetpackcompose.repositories.ServerRepo
21 | import dev.baseio.discordjetpackcompose.repositories.ServerRepoImpl
22 | import javax.inject.Singleton
23 |
24 | @Module
25 | @InstallIn(SingletonComponent::class)
26 | object RepositoryModule {
27 | @Provides
28 | @Singleton
29 | fun provideCountryRepo(@ApplicationContext context: Context): CountryRepo =
30 | CountryRepoImpl(context = context)
31 |
32 | @Provides
33 | @Singleton
34 | fun provideServerRepo(
35 | coroutineDispatcherProvider: CoroutineDispatcherProvider
36 | ): ServerRepo = ServerRepoImpl(coroutineDispatcherProvider = coroutineDispatcherProvider)
37 |
38 | @Provides
39 | @Singleton
40 | fun provideFriendsRepo(): FriendsRepo = FriendsRepoImpl()
41 |
42 |
43 | @Provides
44 | @Singleton
45 | fun provideMessagesRepo(
46 | discordMessageDao: DiscordMessageDao,
47 | entityMapper: EntityMapper,
48 | coroutineDispatcherProvider: CoroutineDispatcherProvider
49 | ): MessagesRepo = MessagesRepoImpl(
50 | discordMessageDao,
51 | entityMapper,
52 | coroutineDispatcherProvider
53 | )
54 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_emoji_4.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
16 |
21 |
26 |
31 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_emoji_1.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/theme/Surface.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.theme
2 |
3 | import androidx.compose.foundation.BorderStroke
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.border
6 | import androidx.compose.foundation.layout.Box
7 | import androidx.compose.material.LocalContentColor
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.CompositionLocalProvider
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.draw.clip
12 | import androidx.compose.ui.draw.shadow
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.graphics.RectangleShape
15 | import androidx.compose.ui.graphics.Shape
16 | import androidx.compose.ui.graphics.compositeOver
17 | import androidx.compose.ui.unit.Dp
18 | import androidx.compose.ui.unit.dp
19 | import androidx.compose.ui.zIndex
20 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider.colors
21 | import kotlin.math.ln
22 |
23 | /**
24 | * An alternative to [androidx.compose.material.Surface]
25 | */
26 | @Composable
27 | fun DiscordSurface(
28 | modifier: Modifier = Modifier,
29 | shape: Shape = RectangleShape,
30 | color: Color = colors.surface,
31 | contentColor: Color = colors.secondary,
32 | border: BorderStroke? = null,
33 | elevation: Dp = 0.dp,
34 | content: @Composable () -> Unit
35 | ) {
36 | Box(
37 | modifier = modifier
38 | .shadow(elevation = elevation, shape = shape, clip = false)
39 | .zIndex(elevation.value)
40 | .then(if (border != null) Modifier.border(border, shape) else Modifier)
41 | .background(
42 | color = getBackgroundColorForElevation(color, elevation),
43 | shape = shape
44 | )
45 | .clip(shape)
46 | ) {
47 | CompositionLocalProvider(LocalContentColor provides contentColor, content = content)
48 | }
49 | }
50 |
51 | @Composable
52 | private fun getBackgroundColorForElevation(
53 | color: Color,
54 | elevation: Dp
55 | ): Color {
56 | return if (elevation > 0.dp
57 | ) {
58 | color.withElevation(elevation)
59 | } else {
60 | color
61 | }
62 | }
63 |
64 | /**
65 | * Applies a [Color.White] overlay to this color based on the [elevation]. This increases visibility
66 | * of elevation for surfaces in a dark theme.
67 | */
68 | private fun Color.withElevation(elevation: Dp): Color {
69 | val foreground = calculateForeground(elevation)
70 | return foreground.compositeOver(this)
71 | }
72 |
73 | /**
74 | * @return the alpha-modified [Color.White] to overlay on top of the surface color to produce
75 | * the resultant color.
76 | */
77 | private fun calculateForeground(elevation: Dp): Color {
78 | val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 20f
79 | return Color.White.copy(alpha = alpha)
80 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/repositories/MessagesRepoImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import androidx.paging.Pager
4 | import androidx.paging.PagingConfig
5 | import androidx.paging.PagingData
6 | import androidx.paging.map
7 | import dev.baseio.discordjetpackcompose.di.dispatcher.CoroutineDispatcherProvider
8 | import dev.baseio.discordjetpackcompose.entities.message.DiscordMessageEntity
9 | import dev.baseio.discordjetpackcompose.entities.message.DiscordUrlMetaEntity
10 | import dev.baseio.discordjetpackcompose.local.dao.DiscordMessageDao
11 | import dev.baseio.discordjetpackcompose.local.model.DBDiscordMessage
12 | import dev.baseio.discordjetpackcompose.mappers.EntityMapper
13 | import kotlinx.coroutines.flow.Flow
14 | import kotlinx.coroutines.flow.mapLatest
15 | import kotlinx.coroutines.withContext
16 | import org.jsoup.Jsoup
17 | import javax.inject.Inject
18 |
19 | class MessagesRepoImpl @Inject constructor(
20 | private val discordMessageDao: DiscordMessageDao,
21 | private val entityMapper: EntityMapper,
22 | private val coroutineMainDispatcherProvider: CoroutineDispatcherProvider
23 | ) : MessagesRepo {
24 |
25 | private val cacheUrlMap = hashMapOf()
26 |
27 | override fun fetchMessages(params: String?): Flow> {
28 | return Pager(PagingConfig(pageSize = 20)) {
29 | discordMessageDao.messagesByDate(params)
30 | }.flow.mapLatest { it -> it.map { entityMapper.mapToDomain(it) } }
31 | }
32 |
33 | override suspend fun sendMessage(params: DiscordMessageEntity): DiscordMessageEntity {
34 | return withContext(coroutineMainDispatcherProvider.io) {
35 | discordMessageDao.insert(entityMapper.mapToData(params))
36 | params
37 | }
38 | }
39 |
40 | override suspend fun fetchUrlMetadata(url: String?): DiscordUrlMetaEntity? {
41 | val discordUrlMetaEntity = DiscordUrlMetaEntity()
42 | if (cacheUrlMap.containsKey(url)) return cacheUrlMap[url!!]
43 |
44 | withContext(coroutineMainDispatcherProvider.io) {
45 | val con = Jsoup.connect(url)
46 | val doc = con.userAgent("Mozilla").get()
47 | val ogTags = doc.select("meta[property^=og:]")
48 | when {
49 | ogTags.size > 0 ->
50 | ogTags.forEachIndexed { index, _ ->
51 | val tag = ogTags[index]
52 | when (tag.attr("property")) {
53 | "og:image" -> discordUrlMetaEntity.image = tag.attr("content")
54 | "og:description" -> discordUrlMetaEntity.desc = tag.attr("content")
55 | "og:url" -> discordUrlMetaEntity.url = tag.attr("content")
56 | "og:title" -> discordUrlMetaEntity.title = tag.attr("content")
57 | }
58 | }
59 | }
60 | cacheUrlMap[url!!] = discordUrlMetaEntity
61 | }
62 | return discordUrlMetaEntity
63 | }
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/onboarding/commonui/OnboardingScreensButton.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.onboarding.commonui
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.ui.graphics.Color
7 | import androidx.compose.foundation.layout.fillMaxWidth
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.material.Button
10 | import androidx.compose.material.ButtonDefaults
11 | import androidx.compose.material.Text
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.ui.Alignment
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.res.colorResource
16 | import androidx.compose.ui.res.stringResource
17 | import androidx.compose.ui.text.font.FontWeight
18 | import androidx.compose.ui.tooling.preview.Preview
19 | import androidx.compose.ui.unit.dp
20 | import dev.baseio.discordjetpackcompose.R
21 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordJetpackComposeTheme
22 | import dev.baseio.discordjetpackcompose.ui.theme.onboarding_button_blue
23 | import dev.baseio.discordjetpackcompose.ui.theme.onboarding_button_grey
24 | import dev.baseio.discordjetpackcompose.ui.theme.white
25 |
26 | @Composable
27 | fun OnboardingScreensButton(
28 | modifier: Modifier = Modifier,
29 | buttonTextProvider: () -> Int,
30 | buttonBackgroundColor: Color = onboarding_button_blue,
31 | buttonWidthFraction: Float = 0.9f,
32 | onClick: () -> Unit
33 | ) {
34 | Button(
35 | onClick = { onClick() },
36 | colors = ButtonDefaults.buttonColors(
37 | backgroundColor = buttonBackgroundColor,
38 | contentColor = colorResource(id = R.color.white)
39 | ),
40 | modifier = modifier
41 | .fillMaxWidth(buttonWidthFraction)
42 | ) {
43 | Text(
44 | text = stringResource(id = buttonTextProvider()),
45 | fontWeight = FontWeight.Bold,
46 | modifier = Modifier.padding(vertical = 4.dp)
47 | )
48 | }
49 | }
50 |
51 |
52 | @Preview(showBackground = true, showSystemUi = true)
53 | @Composable
54 | private fun OnboardingScreensButtonPreview() {
55 | DiscordJetpackComposeTheme {
56 | Column(
57 | modifier = Modifier.background(white),
58 | verticalArrangement = Arrangement.Center,
59 | horizontalAlignment = Alignment.CenterHorizontally
60 | ) {
61 | OnboardingScreensButton(
62 | buttonTextProvider = { R.string.register },
63 | onClick = { }
64 | )
65 | OnboardingScreensButton(
66 | buttonTextProvider = { R.string.login },
67 | buttonBackgroundColor = onboarding_button_grey,
68 | onClick = { }
69 | )
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/notifications/components/MentionsSection.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.components
2 |
3 | import androidx.compose.material.Switch
4 | import androidx.compose.material.Text
5 | import androidx.compose.runtime.*
6 | import androidx.compose.ui.graphics.Color
7 | import androidx.compose.ui.res.stringResource
8 | import androidx.compose.ui.text.AnnotatedString
9 | import androidx.compose.ui.text.SpanStyle
10 | import androidx.compose.ui.text.buildAnnotatedString
11 | import androidx.compose.ui.text.font.FontWeight
12 | import androidx.compose.ui.text.withStyle
13 | import dev.baseio.discordjetpackcompose.R
14 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.serverinfo.DiscordSwitchColors
15 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
16 |
17 | @Composable
18 | fun MentionsSection(
19 | isMute: Boolean
20 | ) {
21 | var suppressEveryoneMentions by remember { mutableStateOf(false) }
22 | var suppressRoleMentions by remember { mutableStateOf(false) }
23 | var suppressPush by remember { mutableStateOf(false) }
24 |
25 | SwitchItem(
26 | onClick = {
27 | suppressEveryoneMentions = !suppressEveryoneMentions
28 | },
29 | title = buildAnnotatedString {
30 | append(stringResource(id = R.string.suppress))
31 | withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)){
32 | append(" "+stringResource(id = R.string.at_everyone))
33 | }
34 | append(" "+stringResource(id = R.string.and))
35 | withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)){
36 | append(" "+stringResource(id = R.string.at_here))
37 | }
38 | },
39 | disabled = false,
40 | isChecked = suppressEveryoneMentions
41 | )
42 | SwitchItem(
43 | onClick = {
44 | suppressRoleMentions = !suppressRoleMentions
45 | },
46 | title = buildAnnotatedString { append(stringResource(id = R.string.role_mentions)) },
47 | disabled = false,
48 | isChecked = suppressRoleMentions
49 | )
50 | SwitchItem(
51 | onClick = {
52 | if (isMute) return@SwitchItem
53 | suppressPush = !suppressPush
54 | },
55 | title = buildAnnotatedString { append(stringResource(id = R.string.mobile_push)) },
56 | disabled = isMute,
57 | isChecked = suppressPush
58 | )
59 | }
60 |
61 | @Composable
62 | fun SwitchItem(
63 | onClick: () -> Unit,
64 | title: AnnotatedString,
65 | disabled: Boolean,
66 | isChecked: Boolean
67 | ) {
68 | SectionItem(
69 | disabled = disabled,
70 | onClick = onClick,
71 | leadingComposable = {
72 | Text(
73 | text = title,
74 | color = DiscordColorProvider.colors.onSurface.copy(alpha = 0.8f)
75 | )
76 | },
77 | trailingComposable = {
78 | Switch(
79 | checked = isChecked,
80 | onCheckedChange = { onClick() },
81 | colors = DiscordSwitchColors
82 | )
83 | }
84 | )
85 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/userSettings/UserSettingsListItem.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Row
6 | import androidx.compose.foundation.layout.Spacer
7 | import androidx.compose.foundation.layout.fillMaxWidth
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.foundation.layout.size
10 | import androidx.compose.material.Icon
11 | import androidx.compose.material.Text
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.ui.Alignment
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.graphics.Color
16 | import androidx.compose.ui.res.painterResource
17 | import androidx.compose.ui.text.style.TextAlign
18 | import androidx.compose.ui.unit.dp
19 | import dev.baseio.discordjetpackcompose.R
20 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.components.models.SettingsEntity
21 | import dev.baseio.discordjetpackcompose.ui.theme.DirectMessageListTypography
22 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider.colors
23 | import dev.baseio.discordjetpackcompose.ui.theme.discord_settings_icon
24 | import dev.baseio.discordjetpackcompose.ui.theme.user_settings_text
25 | import dev.baseio.discordjetpackcompose.ui.utils.clickableWithRipple
26 |
27 | @Composable
28 | fun UserSettingsListItem(
29 | settingsEntity: SettingsEntity,
30 | onItemClick: () -> Unit
31 | ) {
32 | Row(
33 | modifier = Modifier
34 | .fillMaxWidth()
35 | .background(colors.settingsBackground)
36 | .clickableWithRipple(onClick = onItemClick)
37 | .padding(top = 16.dp, bottom = 16.dp, end = 16.dp),
38 | verticalAlignment = Alignment.CenterVertically,
39 | horizontalArrangement = Arrangement.Start
40 | ) {
41 | Icon(
42 | painter = painterResource(id = settingsEntity.icon), contentDescription = null,
43 | modifier = Modifier.padding(start = 16.dp),
44 | tint = discord_settings_icon,
45 | )
46 | Text(
47 | text = settingsEntity.title, style = DirectMessageListTypography.h6,
48 | maxLines = 1,
49 | color = user_settings_text,
50 | modifier = Modifier.padding(start = 24.dp)
51 | )
52 | settingsEntity.currentStatus?.let { status ->
53 | Spacer(modifier = Modifier.weight(1f))
54 | Icon(
55 | painter = painterResource(id = R.drawable.ic_baseline_circle_24),
56 | contentDescription = null,
57 | modifier = Modifier
58 | .size(14.dp)
59 | .align(Alignment.CenterVertically),
60 | tint = Color(0xFF3DA45C)
61 | )
62 | Text(
63 | text = status,
64 | textAlign = TextAlign.End,
65 | modifier = Modifier
66 | .padding(start = 14.dp)
67 | .align(Alignment.CenterVertically)
68 | )
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/components/OnlineIndicator.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.components
2 |
3 | import androidx.compose.animation.animateColorAsState
4 | import androidx.compose.foundation.Canvas
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.BoxScope
7 | import androidx.compose.foundation.layout.Column
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.foundation.layout.size
10 | import androidx.compose.foundation.shape.CircleShape
11 | import androidx.compose.material.Surface
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.runtime.getValue
14 | import androidx.compose.ui.Alignment
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.graphics.Color
17 | import androidx.compose.ui.graphics.drawscope.Fill
18 | import androidx.compose.ui.graphics.drawscope.Stroke
19 | import androidx.compose.ui.tooling.preview.Preview
20 | import androidx.compose.ui.unit.Dp
21 | import androidx.compose.ui.unit.dp
22 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
23 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordJetpackComposeTheme
24 |
25 | @Composable
26 | private fun OnlineIndicator(
27 | modifier: Modifier = Modifier,
28 | isOnline: Boolean,
29 | indicatorSize: Dp = 12.dp,
30 | ) {
31 | val color by animateColorAsState(targetValue = if (isOnline) Color(0xFF3da45c) else Color(0xFF6f7c8a))
32 | Surface(shape = CircleShape, color = DiscordColorProvider.colors.surface) {
33 | Canvas(
34 | modifier = modifier
35 | .padding(1.dp)
36 | .size(indicatorSize),
37 | onDraw = {
38 | drawCircle(
39 | color = color,
40 | radius = if (isOnline) size.width / 2.5f else size.width / 2.75f,
41 | style = if (isOnline) Fill else Stroke(width = size.width.times(0.2f))
42 | )
43 | },
44 | )
45 | }
46 | }
47 |
48 | @Composable
49 | fun OnlineIndicator(
50 | modifier: Modifier = Modifier,
51 | isOnline: Boolean,
52 | indicatorSize: Dp = 12.dp,
53 | indicatorAlignment: Alignment = Alignment.BottomEnd,
54 | content: @Composable BoxScope.() -> Unit = {},
55 | ) {
56 | Box(contentAlignment = indicatorAlignment) {
57 | content()
58 | OnlineIndicator(
59 | modifier = modifier,
60 | isOnline = isOnline,
61 | indicatorSize = indicatorSize
62 | )
63 | }
64 | }
65 |
66 | @Preview(showSystemUi = true)
67 | @Composable
68 | private fun OnlineIndicatorPreview() {
69 | DiscordJetpackComposeTheme {
70 | Column {
71 | OnlineIndicator(isOnline = true)
72 | OnlineIndicator(isOnline = true) {
73 | Surface(
74 | modifier = Modifier.size(48.dp), shape = CircleShape, color = Color(0xFF3da45c)
75 | ) {}
76 | }
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/onboarding/screens/register/RegistrationTypeSelector.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.onboarding.screens.register
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Row
5 | import androidx.compose.foundation.layout.fillMaxHeight
6 | import androidx.compose.foundation.layout.fillMaxWidth
7 | import androidx.compose.foundation.layout.height
8 | import androidx.compose.foundation.shape.RoundedCornerShape
9 | import androidx.compose.material.Button
10 | import androidx.compose.material.ButtonDefaults
11 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
12 | import androidx.compose.material.Text
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.graphics.Color
16 | import androidx.compose.ui.res.stringResource
17 | import androidx.compose.ui.unit.dp
18 | import dev.baseio.discordjetpackcompose.ui.utils.Strings
19 |
20 | @Composable
21 | fun RegistrationTypeSelector(
22 | modifier: Modifier,
23 | selectedOption: RegistrationType,
24 | onSelectionChange: (RegistrationType) -> Unit
25 | ) {
26 | Row(
27 | modifier = modifier
28 | .height(32.dp)
29 | .background(
30 | color = DiscordColorProvider.colors.tabsBackgroundColor,
31 | shape = RoundedCornerShape(4.dp)
32 | )
33 | .fillMaxWidth()
34 | ) {
35 | Button(
36 | modifier = Modifier
37 | .weight(1f)
38 | .fillMaxHeight(),
39 | colors = ButtonDefaults.buttonColors(
40 | backgroundColor = bgColor(selectedOption == RegistrationType.Phone),
41 | contentColor = contentColor(selectedOption == RegistrationType.Phone)
42 | ),
43 | onClick = { onSelectionChange(RegistrationType.Phone) },
44 | ) {
45 | Text(stringResource(id = Strings.phone))
46 | }
47 |
48 | Button(
49 | modifier = Modifier
50 | .weight(1f)
51 | .fillMaxHeight(),
52 | colors = ButtonDefaults.buttonColors(
53 | backgroundColor = bgColor(selected = selectedOption == RegistrationType.Email),
54 | contentColor = contentColor(selected = selectedOption == RegistrationType.Email)
55 | ),
56 | onClick = { onSelectionChange(RegistrationType.Email) },
57 | ) {
58 | Text(stringResource(id = Strings.email))
59 | }
60 | }
61 | }
62 |
63 | @Composable
64 | private fun contentColor(selected: Boolean) =
65 | if (selected) {
66 | Color(0xFFFFFFFF)
67 | } else {
68 | Color(0xFF55555d)
69 | }
70 |
71 | @Composable
72 | private fun bgColor(selected: Boolean) =
73 | if (selected) {
74 | DiscordColorProvider.colors.tabSelectedColor
75 | } else {
76 | DiscordColorProvider.colors.tabsBackgroundColor
77 | }
78 |
79 | sealed class RegistrationType {
80 | object Phone : RegistrationType()
81 | object Email : RegistrationType()
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/search/components/SearchSheetServerSlider.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.search.components
2 |
3 | import android.content.res.Configuration
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.foundation.layout.size
7 | import androidx.compose.foundation.lazy.LazyRow
8 | import androidx.compose.foundation.shape.CircleShape
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.LaunchedEffect
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.draw.clip
13 | import androidx.compose.ui.tooling.preview.Preview
14 | import androidx.compose.ui.unit.dp
15 | import androidx.hilt.navigation.compose.hiltViewModel
16 | import coil.compose.AsyncImage
17 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.components.CountIndicator
18 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordJetpackComposeTheme
19 | import dev.baseio.discordjetpackcompose.ui.utils.clickableWithRipple
20 | import dev.baseio.discordjetpackcompose.ui.utils.rememberCoilImageRequest
21 | import dev.baseio.discordjetpackcompose.viewmodels.DashboardScreenViewModel
22 |
23 | @Composable
24 | fun SearchSheetServerSlider(
25 | serverIdList: List,
26 | dashboardScreenVM: DashboardScreenViewModel = hiltViewModel(),
27 | onServerSelect: (String) -> Unit
28 | ) {
29 | val serverList = dashboardScreenVM.searchSheetServerList
30 |
31 | LaunchedEffect(serverIdList) {
32 | dashboardScreenVM.getServers(serverIds = serverIdList)
33 | }
34 |
35 | LazyRow(
36 | modifier = Modifier.padding(top = 16.dp),
37 | ) {
38 | item { Spacer(modifier = Modifier.padding(start = 8.dp)) }
39 | items(serverList.size) { index ->
40 | DiscordIndicator(
41 | modifier = Modifier.padding(start = 8.dp),
42 | indicatorPosition = DiscordIndicatorPosition.Bottom,
43 | isEnabled = serverList[index].allChannelsUnreadCount > 0
44 | ) {
45 | CountIndicator(count = serverList[index].allChannelsUnreadCount) {
46 | AsyncImage(
47 | model = rememberCoilImageRequest(data = serverList[index].thumbnailUri),
48 | contentDescription = null,
49 | modifier = Modifier
50 | .size(42.dp)
51 | .clip(CircleShape)
52 | .clickableWithRipple { onServerSelect(serverList[index].id) },
53 | )
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
60 | @Preview(showSystemUi = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
61 | @Preview(showSystemUi = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
62 | @Composable
63 | private fun SearchSheetServerSliderPreview() {
64 | DiscordJetpackComposeTheme {
65 | SearchSheetServerSlider(
66 | serverIdList = listOf("1", "2", "3", "4", "5"),
67 | onServerSelect = {}
68 | )
69 | }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/main/chatscreen/ChatScreenContent.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.main.chatscreen
2 |
3 | import androidx.compose.foundation.layout.BoxWithConstraints
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.foundation.layout.imePadding
6 | import androidx.compose.foundation.layout.navigationBarsPadding
7 | import androidx.compose.foundation.layout.statusBarsPadding
8 | import androidx.compose.material.ExperimentalMaterialApi
9 | import androidx.compose.material.ModalBottomSheetState
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.runtime.MutableState
12 | import androidx.compose.runtime.State
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.layout.layoutId
15 | import androidx.compose.ui.unit.dp
16 | import androidx.constraintlayout.compose.ConstraintLayout
17 | import androidx.constraintlayout.compose.ConstraintSet
18 | import dev.baseio.discordjetpackcompose.viewmodels.ChatScreenViewModel
19 |
20 | @OptIn(ExperimentalMaterialApi::class)
21 | @Composable
22 | fun ChatScreenContent(
23 | modifier: Modifier = Modifier,
24 | viewModel: ChatScreenViewModel,
25 | sheetState: ModalBottomSheetState,
26 | isReplyMode: MutableState,
27 | userName: State
28 | ) {
29 | BoxWithConstraints(
30 | modifier = modifier
31 | .statusBarsPadding()
32 | .navigationBarsPadding()
33 | .imePadding()
34 | ) {
35 | val constraints = decoupledConstraints()
36 | ConstraintLayout(constraints) {
37 | ChatMessages(
38 | modifier = Modifier
39 | .layoutId("chatMessages")
40 | .fillMaxSize(),
41 | userName = userName,
42 | viewModel = viewModel,
43 | bottomSheetState = sheetState
44 | )
45 | if (isReplyMode.value) {
46 | ReplyBanner(
47 | modifier = Modifier.layoutId("chatReplyBar"),
48 | userName = userName,
49 | onCloseClicked = {
50 | isReplyMode.value = false
51 | }
52 | )
53 | }
54 | ChatMessageEditor(
55 | modifier = Modifier.layoutId("chatMessageEditor"),
56 | userName = userName,
57 | isReplyMode = isReplyMode,
58 | viewModel = viewModel
59 | )
60 | }
61 | }
62 | }
63 |
64 | private fun decoupledConstraints(): ConstraintSet {
65 | return ConstraintSet {
66 | val chatMessages = createRefFor("chatMessages")
67 | val chatReplyBar = createRefFor("chatReplyBar")
68 | val chatMessageEditor = createRefFor("chatMessageEditor")
69 |
70 | constrain(chatMessages) {
71 | start.linkTo(parent.start)
72 | top.linkTo(parent.top)
73 | end.linkTo(parent.end)
74 | bottom.linkTo(chatMessageEditor.top, margin = 64.dp)
75 | }
76 | constrain(chatReplyBar) {
77 | start.linkTo(parent.start)
78 | end.linkTo(parent.end)
79 | bottom.linkTo(chatMessageEditor.top, margin = 0.dp)
80 | }
81 | constrain(chatMessageEditor) {
82 | start.linkTo(parent.start)
83 | end.linkTo(parent.end)
84 | bottom.linkTo(parent.bottom, margin = 8.dp)
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/notifications/components/NotificationFrequencySection.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.components
2 |
3 | import androidx.compose.material.*
4 | import androidx.compose.runtime.*
5 | import androidx.compose.ui.graphics.Color
6 | import androidx.compose.ui.res.stringResource
7 | import androidx.compose.ui.text.AnnotatedString
8 | import androidx.compose.ui.text.SpanStyle
9 | import androidx.compose.ui.text.buildAnnotatedString
10 | import androidx.compose.ui.text.font.FontWeight
11 | import androidx.compose.ui.text.withStyle
12 | import androidx.compose.ui.unit.dp
13 | import dev.baseio.discordjetpackcompose.R
14 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.models.FrequencyType
15 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
16 |
17 | @Composable
18 | fun NotificationFrequencySection(
19 | isMute: Boolean
20 | ) {
21 | var selectionState by remember { mutableStateOf(FrequencyType.ALL_MESSAGES) }
22 |
23 | RadioSelectionItem(
24 | title = AnnotatedString(stringResource(id = R.string.all_messages)),
25 | onClick = {
26 | if(isMute) return@RadioSelectionItem
27 | selectionState = FrequencyType.ALL_MESSAGES
28 | },
29 | currentSelection = selectionState,
30 | selectionValue = FrequencyType.ALL_MESSAGES,
31 | disabled = isMute
32 | )
33 |
34 | RadioSelectionItem(
35 | title = buildAnnotatedString {
36 | append(stringResource(id = R.string.only))
37 | withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)){
38 | append(" " + stringResource(id = R.string.at_mentions))
39 | }
40 | },
41 | onClick = {
42 | if(isMute) return@RadioSelectionItem
43 | selectionState = FrequencyType.MENTIONS
44 | },
45 | currentSelection = selectionState,
46 | selectionValue = FrequencyType.MENTIONS,
47 | disabled = isMute
48 | )
49 |
50 | RadioSelectionItem(
51 | title = AnnotatedString(stringResource(id = R.string.nothing)),
52 | onClick = {
53 | if(isMute) return@RadioSelectionItem
54 | selectionState = FrequencyType.NOTHING
55 | },
56 | currentSelection = selectionState,
57 | selectionValue = FrequencyType.NOTHING,
58 | disabled = isMute
59 | )
60 |
61 | }
62 |
63 | @Composable
64 | fun RadioSelectionItem(
65 | onClick: () -> Unit,
66 | title: AnnotatedString,
67 | currentSelection: FrequencyType,
68 | selectionValue: FrequencyType,
69 | disabled: Boolean
70 | ) {
71 | SectionItem(
72 | disabled = disabled,
73 | paddingVertical = 4.dp,
74 | onClick = onClick,
75 | leadingComposable = {
76 | Text(
77 | text = title,
78 | color = DiscordColorProvider.colors.onSurface.copy(alpha = 0.8f)
79 | )
80 | },
81 | trailingComposable = {
82 | RadioButton(
83 | selected = currentSelection == selectionValue,
84 | onClick = onClick,
85 | colors = DiscordRadioButtonColors
86 | )
87 | }
88 | )
89 | }
90 |
91 | val DiscordRadioButtonColors @Composable get() = RadioButtonDefaults.colors(
92 | selectedColor = DiscordColorProvider.colors.primary,
93 | unselectedColor = DiscordColorProvider.colors.onSurface.copy(alpha = 0.5f)
94 | )
95 |
96 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/userSettings/UserSettingsList.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.foundation.lazy.LazyColumn
7 | import androidx.compose.foundation.lazy.rememberLazyListState
8 | import androidx.compose.material.TabRowDefaults.Divider
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.res.stringResource
13 | import androidx.compose.ui.unit.dp
14 | import com.google.accompanist.insets.navigationBarsPadding
15 | import dev.baseio.discordjetpackcompose.R.string
16 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.components.GetSettingsSubtitle
17 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.components.GetTopComponent
18 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.components.models.SettingsEntity
19 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider.colors
20 |
21 | @Composable
22 | fun UserSettingsList(
23 | userSettings: List,
24 | nitroSettings: List,
25 | appSettings: List,
26 | appInfo: List,
27 | profileImageUrl: String,
28 | username: String = "mutual",
29 | discordTag: String = "#8976"
30 | ) {
31 | val lazyListState = rememberLazyListState()
32 |
33 | LazyColumn(state = lazyListState, modifier = Modifier.background(colors.settingsBackground)) {
34 | item {
35 | GetTopComponent(lazyListState, profileImageUrl, username, discordTag)
36 | }
37 |
38 | item {
39 | GetSettingsSubtitle(title = stringResource(string.settings_user_settings))
40 | }
41 |
42 | items(userSettings.size) { index ->
43 | UserSettingsListItem(settingsEntity = userSettings[index],
44 | onItemClick = {})
45 | }
46 |
47 | item {
48 | Divider(color = Color.DarkGray, thickness = 1.dp, modifier = Modifier.padding(top = 8.dp))
49 | GetSettingsSubtitle(title = stringResource(string.settings_nitro_settings))
50 | }
51 |
52 | items(nitroSettings.size) { index ->
53 | UserSettingsListItem(settingsEntity = nitroSettings[index],
54 | onItemClick = {})
55 | }
56 |
57 | item {
58 | Divider(color = Color.DarkGray, thickness = 1.dp, modifier = Modifier.padding(top = 8.dp))
59 | GetSettingsSubtitle(title = stringResource(string.settings_app_settings))
60 | }
61 |
62 | items(appSettings.size) { index ->
63 | UserSettingsListItem(settingsEntity = appSettings[index],
64 | onItemClick = {})
65 | }
66 |
67 | item {
68 | Divider(color = Color.DarkGray, thickness = 1.dp, modifier = Modifier.padding(top = 8.dp))
69 | GetSettingsSubtitle(title = stringResource(string.settings_app_info))
70 | }
71 |
72 | items(appInfo.size) { index ->
73 | UserSettingsListItem(settingsEntity = appInfo[index],
74 | onItemClick = {})
75 | }
76 |
77 | item {
78 | Spacer(
79 | modifier = Modifier
80 | .navigationBarsPadding()
81 | .padding(vertical = 32.dp)
82 | )
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/utils/SampleData.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.utils
2 |
3 | import androidx.compose.material.icons.Icons
4 | import androidx.compose.material.icons.filled.Tag
5 | import dev.baseio.discordjetpackcompose.entities.search.SearchFilter
6 | import dev.baseio.discordjetpackcompose.entities.search.SearchSheetListItemEntity
7 | import dev.baseio.discordjetpackcompose.entities.server.ChannelEntity
8 | import dev.baseio.discordjetpackcompose.entities.server.ChannelType
9 | import dev.baseio.discordjetpackcompose.entities.server.ServerEntity
10 |
11 | fun getSampleServer(serverId: String) = ServerEntity(
12 | id = serverId,
13 | name = "Test Server$serverId",
14 | posterUri = "https://images.pexels.com/photos/12641743/pexels-photo-12641743.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
15 | channels = listOf(
16 | ChannelEntity(
17 | id = "1",
18 | name = "announcements",
19 | type = ChannelType.PUBLIC,
20 | category = "INFO",
21 | unreadCount = 92,
22 | isUnread = true,
23 | ),
24 | ChannelEntity(
25 | id = "2",
26 | name = "youtube-feed",
27 | type = ChannelType.PUBLIC,
28 | category = "INFO",
29 | ),
30 | ChannelEntity(
31 | id = "3",
32 | name = "Total Members: 6.71K",
33 | type = ChannelType.PRIVATE,
34 | ),
35 | ChannelEntity(
36 | id = "4",
37 | name = "Online Members: 366",
38 | type = ChannelType.PRIVATE,
39 | ),
40 | ChannelEntity(
41 | id = "5",
42 | name = "android",
43 | type = ChannelType.PUBLIC,
44 | category = "PROGRAMMING",
45 | isUnread = true,
46 | ),
47 | ),
48 | hasNitroSubscription = serverId.toIntOrNull()?.rem(2) == 0,
49 | onlineMembersCount = 415,
50 | totalMembersCount = 1009,
51 | serverEmojiUris = mutableListOf().apply {
52 | repeat(16) {
53 | add("https://cdn.shopify.com/s/files/1/1061/1924/files/Hugging_Face_Emoji_2028ce8b-c213-4d45-94aa-21e1a0842b4d_large.png?15202324258887420558")
54 | }
55 | },
56 | boostCount = 7,
57 | )
58 |
59 | fun getSampleSheetListItems() = mutableListOf().apply {
60 | repeat(10) { index ->
61 | add(
62 | SearchSheetListItemEntity(
63 | id = "channel1$index",
64 | itemType = SearchFilter.TextChannels,
65 | iconUri = Icons.Default.Tag,
66 | title = "announcements$index",
67 | subtitle = "!INFO",
68 | serverName = "Coding in Flow",
69 | unreadCount = 92
70 | )
71 | )
72 | add(
73 | SearchSheetListItemEntity(
74 | id = "channel2$index",
75 | itemType = SearchFilter.Users,
76 | iconUri = Constants.MMLogoUrl,
77 | title = "YouTube$index",
78 | subtitle = null,
79 | serverName = "Youtube#1316",
80 | unreadCount = null
81 | )
82 | )
83 | }
84 | }
85 |
86 | fun getSampleServerList() = listOf(
87 | getSampleServer(serverId = "1"),
88 | getSampleServer(serverId = "2"),
89 | )
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/serverinfo/components/ServerQuickActions.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.serverinfo.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.Row
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material.ButtonDefaults
7 | import androidx.compose.material.Icon
8 | import androidx.compose.material.Text
9 | import androidx.compose.material.TextButton
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Alignment
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.res.painterResource
15 | import androidx.compose.ui.res.stringResource
16 | import androidx.compose.ui.text.font.FontWeight
17 | import androidx.compose.ui.tooling.preview.Preview
18 | import androidx.compose.ui.unit.dp
19 | import dev.baseio.discordjetpackcompose.R
20 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.serverinfo.components.models.ServerQuickAction
21 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
22 | import dev.baseio.discordjetpackcompose.ui.theme.ServerInfoTypography
23 | import dev.baseio.discordjetpackcompose.ui.theme.contentColorFor
24 |
25 | @Composable
26 | fun ServerQuickActions(
27 | modifier: Modifier = Modifier, actions: List
28 | ) {
29 | Row(
30 | modifier = modifier, verticalAlignment = Alignment.CenterVertically
31 | ) {
32 | actions.forEach { action ->
33 | TextButton(onClick = action.onClick, modifier = Modifier.weight(1f), colors = ButtonDefaults.textButtonColors(
34 | contentColor = DiscordColorProvider.colors.contentColorFor(DiscordColorProvider.colors.primary)
35 | )) {
36 | Column(
37 | horizontalAlignment = Alignment.CenterHorizontally
38 | ) {
39 | Icon(
40 | painter = painterResource(id = action.icon),
41 | contentDescription = null,
42 | tint = action.iconTint
43 | ?: DiscordColorProvider.colors.onSurface.copy(alpha = 0.5f)
44 | )
45 | Text(
46 | text = action.label,
47 | modifier = Modifier.padding(vertical = 8.dp),
48 | style = ServerInfoTypography.caption.copy(fontWeight = FontWeight.Bold)
49 | )
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
56 | @Preview(showSystemUi = true)
57 | @Composable
58 | private fun ServerQuickActionsPreview() {
59 | ServerQuickActions(
60 | actions = listOf(
61 | ServerQuickAction(
62 | icon = R.drawable.ic_boost,
63 | iconTint = Color.Magenta,
64 | label = "3 Boosts",
65 | onClick = {},
66 | ),
67 | ServerQuickAction(
68 | icon = R.drawable.ic_notifications,
69 | label = stringResource(R.string.server_info_notifications_btn_txt),
70 | onClick = {},
71 | ),
72 | ServerQuickAction(
73 | icon = R.drawable.ic_invite,
74 | label = stringResource(R.string.server_info_invite_btn_txt),
75 | onClick = {},
76 | ),
77 | )
78 | )
79 | }
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/serverchannels/ServerChannelList.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard.serverchannels
2 |
3 | import androidx.compose.animation.AnimatedContent
4 | import androidx.compose.animation.ExperimentalAnimationApi
5 | import androidx.compose.animation.fadeIn
6 | import androidx.compose.animation.fadeOut
7 | import androidx.compose.animation.with
8 | import androidx.compose.foundation.layout.fillMaxHeight
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.material.Surface
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.LaunchedEffect
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.tooling.preview.Preview
15 | import androidx.compose.ui.unit.dp
16 | import androidx.hilt.navigation.compose.hiltViewModel
17 | import dev.baseio.discordjetpackcompose.entities.ChatUserEntity
18 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.channels.ChannelList
19 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.components.ServerIconSelector
20 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.directmessages.DirectMessageList
21 | import dev.baseio.discordjetpackcompose.ui.theme.DiscordColorProvider
22 | import dev.baseio.discordjetpackcompose.viewmodels.DashboardScreenViewModel
23 |
24 | @OptIn(ExperimentalAnimationApi::class)
25 | @Composable
26 | fun ServerChannelList(
27 | modifier: Modifier = Modifier,
28 | serverId: String,
29 | chatUserList: List,
30 | dashboardScreenVM: DashboardScreenViewModel = hiltViewModel(),
31 | openServerInfoBottomSheet: () -> Unit,
32 | onItemSelection: () -> Unit
33 | ) {
34 | val serverState = dashboardScreenVM.currentServerEntity
35 |
36 | Surface(
37 | modifier = modifier.fillMaxHeight(),
38 | elevation = 4.dp,
39 | color = DiscordColorProvider.colors.background,
40 | ) {
41 | AnimatedContent(
42 | targetState = serverId,
43 | transitionSpec = { fadeIn() with fadeOut() }
44 | ) { selectedScreenId ->
45 | when (selectedScreenId) {
46 | ServerIconSelector.DMScreenId -> DirectMessageList(
47 | modifier = Modifier.fillMaxSize(),
48 | openNewDMScreen = {} , // todo: Not implemented
49 | openSearchScreen = {},
50 | onItemSelection = onItemSelection,
51 | chats = chatUserList,
52 | viewModel = dashboardScreenVM
53 | )
54 | else -> ChannelList(
55 | modifier = Modifier.fillMaxSize(),
56 | serverState = serverState,
57 | onInviteButtonClick = { serverId -> }, // TODO: Not implemented
58 | openServerInfoBottomSheet = openServerInfoBottomSheet,
59 | onItemSelection = onItemSelection
60 | )
61 | }
62 | }
63 | }
64 | }
65 |
66 | @Preview(showSystemUi = true)
67 | @Composable
68 | private fun ServerChannelListPreview() {
69 | val vm = hiltViewModel()
70 | LaunchedEffect(Unit) {
71 | vm.getServer("testId")
72 | }
73 | ServerChannelList(
74 | serverId = ServerIconSelector.DMScreenId,
75 | chatUserList = emptyList(),
76 | dashboardScreenVM = hiltViewModel(),
77 | openServerInfoBottomSheet = {}
78 | ) {}
79 | }
--------------------------------------------------------------------------------
/navigator/src/main/java/dev/baseio/discordjetpackcompose/navigator/Navigator.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.navigator
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.Observer
5 | import androidx.navigation.NavController
6 | import androidx.navigation.NavOptionsBuilder
7 | import kotlinx.coroutines.*
8 | import kotlinx.coroutines.channels.Channel
9 | import kotlinx.coroutines.flow.*
10 |
11 | abstract class Navigator {
12 | val navigationCommands = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE)
13 |
14 | // We use a StateFlow here to allow ViewModels to start observing navigation results before the initial composition,
15 | // and still get the navigation result later
16 | val navControllerFlow = MutableStateFlow(null)
17 |
18 | fun navigateUp() {
19 | navigationCommands.tryEmit(NavigationCommand.NavigateUp)
20 | }
21 |
22 | }
23 |
24 | abstract class ComposeNavigator : Navigator() {
25 | abstract fun navigate(route: String, optionsBuilder: (NavOptionsBuilder.() -> Unit)? = null)
26 | abstract fun observeResult(key: String, route: String? = null): Flow
27 | abstract fun navigateBackWithResult(key: String, result: T, route: String?)
28 |
29 | abstract fun popUpTo(route: String, inclusive: Boolean)
30 | abstract fun navigateAndClearBackStack(route: String)
31 |
32 | suspend fun handleNavigationCommands(navController: NavController) {
33 | navigationCommands
34 | .onSubscription { this@ComposeNavigator.navControllerFlow.value = navController }
35 | .onCompletion { this@ComposeNavigator.navControllerFlow.value = null }
36 | .collect { navController.handleComposeNavigationCommand(it) }
37 | }
38 |
39 | private fun NavController.handleComposeNavigationCommand(navigationCommand: NavigationCommand) {
40 | when (navigationCommand) {
41 | is ComposeNavigationCommand.NavigateToRoute -> {
42 | navigate(navigationCommand.route, navigationCommand.options)
43 | }
44 | NavigationCommand.NavigateUp -> navigateUp()
45 | is ComposeNavigationCommand.PopUpToRoute -> popBackStack(
46 | navigationCommand.route,
47 | navigationCommand.inclusive
48 | )
49 | is ComposeNavigationCommand.NavigateUpWithResult<*> -> {
50 | navUpWithResult(navigationCommand)
51 | }
52 | else -> {
53 | throw RuntimeException("can't handle this with ComposeNavigator")
54 | }
55 | }
56 | }
57 |
58 | private fun NavController.navUpWithResult(navigationCommand: ComposeNavigationCommand.NavigateUpWithResult<*>) {
59 | val backStackEntry =
60 | navigationCommand.route?.let { getBackStackEntry(it) }
61 | ?: previousBackStackEntry
62 | backStackEntry?.savedStateHandle?.set(
63 | navigationCommand.key,
64 | navigationCommand.result
65 | )
66 |
67 | navigationCommand.route?.let {
68 | popBackStack(it, false)
69 | } ?: run {
70 | navigateUp()
71 | }
72 | }
73 | }
74 |
75 |
76 | @OptIn(DelicateCoroutinesApi::class)
77 | fun LiveData.asFlow(): Flow = flow {
78 | val channel = Channel(Channel.CONFLATED)
79 | val observer = Observer {
80 | channel.trySend(it)
81 | }
82 | withContext(Dispatchers.Main.immediate) {
83 | observeForever(observer)
84 | }
85 | try {
86 | for (value in channel) {
87 | emit(value)
88 | }
89 | } finally {
90 | GlobalScope.launch(Dispatchers.Main.immediate) {
91 | removeObserver(observer)
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/benchmark/src/main/java/dev/baseio/discordjetpackcompose/startup/StartupBenchmark.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package dev.baseio.discordjetpackcompose.startup
18 |
19 | import androidx.benchmark.macro.BaselineProfileMode.Disable
20 | import androidx.benchmark.macro.BaselineProfileMode.Require
21 | import androidx.benchmark.macro.CompilationMode
22 | import androidx.benchmark.macro.StartupMode
23 | import androidx.benchmark.macro.StartupMode.COLD
24 | import androidx.benchmark.macro.StartupMode.HOT
25 | import androidx.benchmark.macro.StartupMode.WARM
26 | import androidx.benchmark.macro.StartupTimingMetric
27 | import androidx.benchmark.macro.junit4.MacrobenchmarkRule
28 | import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
29 | import org.junit.Rule
30 | import org.junit.Test
31 | import org.junit.runner.RunWith
32 |
33 | /**
34 | * Run this benchmark from Studio to see startup measurements, and captured system traces
35 | * for investigating your app's performance from a cold state.
36 | */
37 | @RunWith(AndroidJUnit4ClassRunner::class)
38 | class ColdStartupBenchmark : AbstractStartupBenchmark(COLD)
39 |
40 | /**
41 | * Run this benchmark from Studio to see startup measurements, and captured system traces
42 | * for investigating your app's performance from a warm state.
43 | */
44 | @RunWith(AndroidJUnit4ClassRunner::class)
45 | class WarmStartupBenchmark : AbstractStartupBenchmark(WARM)
46 |
47 | /**
48 | * Run this benchmark from Studio to see startup measurements, and captured system traces
49 | * for investigating your app's performance from a hot state.
50 | */
51 | @RunWith(AndroidJUnit4ClassRunner::class)
52 | class HotStartupBenchmark : AbstractStartupBenchmark(HOT)
53 |
54 | /**
55 | * Base class for benchmarks with different startup modes.
56 | * Enables app startups from various states of baseline profile or [CompilationMode]s.
57 | */
58 | abstract class AbstractStartupBenchmark(private val startupMode: StartupMode) {
59 | @get:Rule
60 | val benchmarkRule = MacrobenchmarkRule()
61 |
62 | @Test
63 | fun startupNoCompilation() = startup(CompilationMode.None())
64 |
65 | @Test
66 | fun startupBaselineProfileDisabled() = startup(
67 | CompilationMode.Partial(baselineProfileMode = Disable, warmupIterations = 1)
68 | )
69 |
70 | @Test
71 | fun startupBaselineProfile() = startup(CompilationMode.Partial(baselineProfileMode = Require))
72 |
73 | @Test
74 | fun startupFullCompilation() = startup(CompilationMode.Full())
75 |
76 | private fun startup(compilationMode: CompilationMode) = benchmarkRule.measureRepeated(
77 | packageName = "dev.baseio.discordjetpackcompose",
78 | metrics = listOf(StartupTimingMetric()),
79 | compilationMode = compilationMode,
80 | iterations = 10,
81 | startupMode = startupMode,
82 | setupBlock = {
83 | pressHome()
84 | }
85 | ) {
86 | startActivityAndWait()
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/dev/baseio/discordjetpackcompose/ui/routes/dashboard/DashboardRoute.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.ui.routes.dashboard
2 |
3 | import androidx.compose.material.ExperimentalMaterialApi
4 | import androidx.compose.material.ModalBottomSheetState
5 | import androidx.navigation.NavGraphBuilder
6 | import androidx.navigation.compose.composable
7 | import androidx.navigation.navigation
8 | import dev.baseio.discordjetpackcompose.entities.server.ServerEntity
9 | import dev.baseio.discordjetpackcompose.navigator.ComposeNavigator
10 | import dev.baseio.discordjetpackcompose.navigator.DiscordRoute
11 | import dev.baseio.discordjetpackcompose.navigator.DiscordScreen
12 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.createServer.CreateServer
13 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.friends.FriendsScreen
14 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.invite.InviteScreen
15 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.main.HomeScreen
16 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.main.dasboard.DashboardScreen
17 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.notifications.NotificationScreen
18 | import dev.baseio.discordjetpackcompose.ui.routes.dashboard.userSettings.UserSettings
19 |
20 | fun NavGraphBuilder.dashboardRoute(
21 | composeNavigator: ComposeNavigator,
22 | ) {
23 | navigation(
24 | startDestination = DiscordScreen.Dashboard.name,
25 | route = DiscordRoute.Dashboard.name
26 | ) {
27 | composable(DiscordScreen.Dashboard.name) {
28 | DashboardScreen(composeNavigator = composeNavigator)
29 | }
30 | composable(DiscordScreen.Friends.name) {
31 | FriendsScreen(composeNavigator = composeNavigator)
32 | }
33 | composable(DiscordScreen.Invite.name) {
34 | InviteScreen(composeNavigator = composeNavigator)
35 | }
36 | composable(DiscordScreen.CreateServer.name) {
37 | CreateServer(composeNavigator)
38 | }
39 | composable(DiscordScreen.NotificationSettings.name) {
40 | NotificationScreen(composeNavigator = composeNavigator)
41 | }
42 | composable(DiscordScreen.UserSettings.name) {
43 | UserSettings(composeNavigator = composeNavigator)
44 | }
45 | }
46 |
47 | }
48 |
49 | @OptIn(ExperimentalMaterialApi::class)
50 | fun NavGraphBuilder.setupDashboardBottomNavScreens(
51 | composeNavigator: ComposeNavigator,
52 | sheetState: ModalBottomSheetState,
53 | onSelectServer: (String) -> Unit,
54 | shouldDisplayBottomBar: (Boolean) -> Unit,
55 | serverList: List // todo: refer a single source of truth for this serverlist without passing it around
56 | ) {
57 | navigation(
58 | startDestination = DiscordScreen.Home.route,
59 | route = DiscordRoute.DashboardBottomNav.name
60 | ) {
61 | composable(DiscordScreen.Home.route) {
62 | HomeScreen(
63 | composeNavigator = composeNavigator,
64 | serverList = serverList,
65 | onSelectServer = onSelectServer,
66 | sheetState = sheetState,
67 | shouldDisplayBottomBar = shouldDisplayBottomBar
68 | )
69 | }
70 | composable(DiscordScreen.Friends.route) {
71 | FriendsScreen(composeNavigator = composeNavigator)
72 | }
73 | composable(DiscordScreen.UserSettings.name) {
74 | UserSettings(composeNavigator = composeNavigator)
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/data/src/main/java/dev/baseio/discordjetpackcompose/repositories/ServerRepoImpl.kt:
--------------------------------------------------------------------------------
1 | package dev.baseio.discordjetpackcompose.repositories
2 |
3 | import dev.baseio.discordjetpackcompose.di.dispatcher.CoroutineDispatcherProvider
4 | import dev.baseio.discordjetpackcompose.entities.NetworkState
5 | import dev.baseio.discordjetpackcompose.entities.search.SearchSheetListItemEntity
6 | import dev.baseio.discordjetpackcompose.entities.server.ChannelEntity
7 | import dev.baseio.discordjetpackcompose.entities.server.ChannelType
8 | import dev.baseio.discordjetpackcompose.entities.server.ServerEntity
9 | import dev.baseio.discordjetpackcompose.utils.getSampleServerList
10 | import dev.baseio.discordjetpackcompose.utils.getSampleSheetListItems
11 | import dev.baseio.discordjetpackcompose.utils.safeApiCall
12 |
13 | class ServerRepoImpl(private val coroutineDispatcherProvider: CoroutineDispatcherProvider) :
14 | ServerRepo {
15 | override suspend fun getServer(serverId: String): NetworkState {
16 | return safeApiCall(
17 | coroutineDispatcherProvider = coroutineDispatcherProvider,
18 | debugResponse = ServerEntity(
19 | id = serverId,
20 | name = "Test Server",
21 | posterUri = "https://images.pexels.com/photos/12641743/pexels-photo-12641743.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
22 | channels = listOf(
23 | ChannelEntity(
24 | id = "1",
25 | name = "announcements",
26 | type = ChannelType.PUBLIC,
27 | category = "INFO",
28 | unreadCount = 92,
29 | isUnread = true,
30 | ),
31 | ChannelEntity(
32 | id = "2",
33 | name = "youtube-feed",
34 | type = ChannelType.PUBLIC,
35 | category = "INFO",
36 | ),
37 | ChannelEntity(
38 | id = "3",
39 | name = "Total Members: 6.71K",
40 | type = ChannelType.PRIVATE,
41 | ),
42 | ChannelEntity(
43 | id = "4",
44 | name = "Online Members: 366",
45 | type = ChannelType.PRIVATE,
46 | ),
47 | ChannelEntity(
48 | id = "5",
49 | name = "android",
50 | type = ChannelType.PUBLIC,
51 | category = "PROGRAMMING",
52 | isUnread = true,
53 | ),
54 | ),
55 | hasNitroSubscription = true
56 | )
57 | ) {
58 | TODO("Not Implemented")
59 | }
60 | }
61 |
62 | override suspend fun getServerList(): NetworkState> {
63 | return safeApiCall(
64 | coroutineDispatcherProvider = coroutineDispatcherProvider,
65 | debugResponse = getSampleServerList()
66 | ) {
67 | TODO("Not Implemented")
68 | }
69 | }
70 |
71 | override suspend fun getSearchSheetItemList(): NetworkState> {
72 | return safeApiCall(
73 | coroutineDispatcherProvider = coroutineDispatcherProvider,
74 | debugResponse = getSampleSheetListItems()
75 | ) {
76 | TODO("Not Implemented")
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------