├── .editorconfig ├── .github ├── CODEOWNERS ├── dependabot.yml ├── pull_request_template.md └── workflows │ └── android.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── benchmark-rules.pro ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ ├── MainActivity.kt │ ├── TwitchCloneApp.kt │ ├── initializer │ ├── AssetsInitializer.kt │ ├── MainInitializer.kt │ ├── StreamChatInitializer.kt │ ├── StreamLogInitializer.kt │ └── StreamVideoInitializer.kt │ ├── navigation │ ├── NavSlideComposable.kt │ ├── TwitchNavHost.kt │ └── TwitchNavigation.kt │ └── ui │ └── TwitchCloneMain.kt ├── baselineprofile ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── baselineprofile │ └── BaselineProfileGenerator.kt ├── build-logic ├── convention │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── AndroidApplicationComposeConventionPlugin.kt │ │ ├── AndroidApplicationConventionPlugin.kt │ │ ├── AndroidFeatureConventionPlugin.kt │ │ ├── AndroidHiltConventionPlugin.kt │ │ ├── AndroidLibraryComposeConventionPlugin.kt │ │ ├── AndroidLibraryConventionPlugin.kt │ │ ├── SpotlessConventionPlugin.kt │ │ └── io │ │ └── getstream │ │ └── chat │ │ └── android │ │ └── twitchclone │ │ ├── AndroidCompose.kt │ │ └── KotlinAndroid.kt ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties └── settings.gradle.kts ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── Configuration.kt ├── core-data ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── data │ ├── coroutines │ └── WhileSubscribedOrRetained.kt │ ├── di │ └── DataModule.kt │ ├── mapper │ └── Mappers.kt │ └── repository │ ├── RewardRepository.kt │ └── RewardRepositoryImpl.kt ├── core-database ├── .gitignore ├── build.gradle.kts ├── schemas │ └── io.getstream.chat.android.twitchclone.database.TwitchDatabase │ │ └── 1.json └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── database │ ├── TwitchDatabase.kt │ ├── dao │ └── RewardDao.kt │ ├── di │ └── DatabaseModule.kt │ └── entity │ └── RewardEntity.kt ├── core-designsystem ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── badges │ │ ├── cat.gif │ │ ├── cat2.gif │ │ ├── cool.gif │ │ ├── gold.png │ │ ├── prime.png │ │ └── star.png │ ├── emotes │ │ ├── angelthump.png │ │ ├── ariw.png │ │ ├── brobalt.png │ │ ├── bttvnice.png │ │ ├── burself.png │ │ ├── candianrage.png │ │ ├── cigrip.png │ │ ├── concerndoge.png │ │ ├── cruw.png │ │ ├── cvhazmat.png │ │ ├── cvl.png │ │ ├── cvmask.png │ │ ├── cvr.png │ │ ├── datsauce.png │ │ ├── dogchamp.png │ │ ├── duckerz.png │ │ ├── feelsamazingman.png │ │ ├── feelsbadman.png │ │ ├── feelsbirthdayman.png │ │ ├── feelsgoodman.png │ │ ├── feelssnowman.png │ │ ├── feelssnowyman.png │ │ ├── firespeed.png │ │ ├── fishmoley.png │ │ ├── foreveralone.png │ │ ├── gaben.png │ │ ├── hahaa.png │ │ ├── hailhelix.png │ │ ├── hhhehehe.png │ │ ├── icecold.png │ │ ├── kappacool.png │ │ ├── karappa.png │ │ ├── kkona.png │ │ ├── lul.png │ │ ├── m&mjc.png │ │ ├── monkas.png │ │ ├── nam.png │ │ ├── notsquishy.png │ │ ├── poledoge.png │ │ ├── rarepepe.png │ │ ├── ronsmug.png │ │ ├── saltycorn.png │ │ ├── shoopdawhoop.png │ │ ├── sosgame.png │ │ ├── sqshy.png │ │ ├── taxibro.png │ │ ├── tehpolecat.png │ │ ├── twat.png │ │ ├── vapenation.png │ │ ├── vislaud.png │ │ ├── watchusay.png │ │ ├── wowee.png │ │ └── wubtf.png │ └── gifs │ │ ├── aaugh.gif │ │ ├── ayayajam.gif │ │ ├── balddab.gif │ │ ├── baldflick.gif │ │ ├── baldfloss.gif │ │ ├── baldspin.gif │ │ ├── baldyappp.gif │ │ ├── baldyikes.gif │ │ ├── baners.gif │ │ ├── bropls.gif │ │ ├── catjam.gif │ │ ├── checkers.gif │ │ ├── click.gif │ │ ├── coomtime.gif │ │ ├── crawlers.gif │ │ ├── deadlole.gif │ │ ├── deskchan.gif │ │ ├── eato.gif │ │ ├── eddiebaldmansmash.gif │ │ ├── eddieknead.gif │ │ ├── eddiespin.gif │ │ ├── eekum.gif │ │ ├── flappers.gif │ │ ├── fubaldi.gif │ │ ├── gamba.gif │ │ ├── gawkgawk.gif │ │ ├── guitartime.gif │ │ ├── humpers.gif │ │ ├── hypernodders.gif │ │ ├── hypernopers.gif │ │ ├── hyperpeepod.gif │ │ ├── johnsouls.gif │ │ ├── kissabrother.gif │ │ ├── kissapregomie.gif │ │ ├── komodochomp.gif │ │ ├── lgiggle.gif │ │ ├── mariorun.gif │ │ ├── moon2bass.gif │ │ ├── noted.gif │ │ ├── peepeegachat.gif │ │ ├── peepees.gif │ │ ├── peepersd.gif │ │ ├── peepoboom.gif │ │ ├── peepocheering.gif │ │ ├── peepogolfclap.gif │ │ ├── peeponarusprint.gif │ │ ├── peeposhy.gif │ │ ├── peeposteer.gif │ │ ├── pepelepsy.gif │ │ ├── pepemetal.gif │ │ ├── petthebaldie.gif │ │ ├── pettheeddie.gif │ │ ├── pettheqynoa.gif │ │ ├── pukers.gif │ │ ├── qynoalurks.gif │ │ ├── raremoon.gif │ │ ├── refracting.gif │ │ ├── robpls.gif │ │ ├── shruggers.gif │ │ ├── shushers.gif │ │ ├── slap.gif │ │ ├── sneakers.gif │ │ ├── solarflare.gif │ │ ├── soulshroom.gif │ │ ├── speeders.gif │ │ ├── tanties.gif │ │ ├── teatime.gif │ │ ├── teatime2.gif │ │ ├── tinyteeth.gif │ │ ├── twerkers.gif │ │ ├── vanish.gif │ │ ├── vibers.gif │ │ ├── wowers.gif │ │ └── yappp.gif │ ├── kotlin │ └── io │ │ └── getstream │ │ └── chat │ │ └── android │ │ └── twitchclone │ │ └── designsystem │ │ ├── Themes.kt │ │ ├── TwitchLoadingIndicator.kt │ │ └── assets │ │ ├── AssetUtils.kt │ │ └── AssetsProvider.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── ic_emoji.xml │ ├── ic_gear.xml │ ├── ic_half_moon.xml │ ├── ic_heart.xml │ ├── ic_info.xml │ ├── ic_launcher_background.xml │ ├── ic_sun.xml │ ├── ic_vertical_dots.xml │ └── reward_ic.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── values-night │ └── themes.xml │ └── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml ├── core-model ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── model │ ├── ChannelExtensions.kt │ └── Reward.kt ├── core-navigation ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── navigation │ ├── LocalNavigation.kt │ ├── NavigationCommand.kt │ ├── Navigator.kt │ ├── TwitchCloneComposeNavigator.kt │ └── TwitchScreens.kt ├── core-network ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── network │ ├── TwitchDispatchers.kt │ ├── di │ ├── DispatchersModule.kt │ └── NetworkModule.kt │ └── service │ └── RewardsService.kt ├── feature-channels ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── channels │ ├── ChannelInformation.kt │ ├── StreamerInformation.kt │ ├── TwitchChannelItem.kt │ ├── TwitchChannels.kt │ └── grids │ ├── GridChannelList.kt │ └── GridChannels.kt ├── feature-livestream ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── livestream │ ├── EnsurePermissions.kt │ ├── LivestreamStreamer.kt │ ├── LivestreamViewModel.kt │ ├── LivestreamWatcher.kt │ └── StreamerInformation.kt ├── feature-messages ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── io │ └── getstream │ └── chat │ └── android │ └── twitchclone │ └── messages │ ├── extensions │ ├── MessageExtensions.kt │ └── UserExtensions.kt │ └── ui │ ├── emotes │ └── EmotesContent.kt │ ├── messages │ ├── ChannelDescription.kt │ ├── ChatSettingsIcon.kt │ ├── SendButton.kt │ ├── TwitchBottomBar.kt │ ├── TwitchMessageItem.kt │ ├── TwitchMessages.kt │ └── TwitchMessagesViewModel.kt │ ├── player │ ├── TwitchVideoPlayer.kt │ └── VideoPlayer.kt │ └── rewards │ ├── RewardExtension.kt │ ├── RewardIntegration.kt │ ├── RewardItem.kt │ └── RewardsContent.kt ├── figures ├── figure0.png ├── figure1.png ├── figure2.png ├── figure3.png ├── figure4.png ├── stream0.png ├── stream1.png ├── stream2.png ├── stream3.png ├── stream4.png ├── stream5.png └── stream6.png ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── previews ├── preview0.png ├── preview1.png ├── preview2.png ├── preview3.png ├── preview4.png └── preview5.png ├── secrets.defaults.properties ├── secrets.properties ├── settings.gradle.kts └── spotless ├── copyright.kt ├── copyright.kts ├── copyright.xml └── spotless.gradle /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | # Most of the standard properties are supported 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | max_line_length = 100 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # More details are here: https://help.github.com/articles/about-codeowners/ 5 | 6 | # The '*' pattern is global owners. 7 | # Not adding in this PR, but I'd like to try adding a global owner set with the entire team. 8 | # One interpretation of their docs is that global owners are added only if not removed 9 | # by a more local rule. 10 | 11 | # Order is important. The last matching pattern has the most precedence. 12 | # The folders are ordered as follows: 13 | 14 | # In each subsection folders are ordered first by depth, then alphabetically. 15 | # This should make it easy to add new rules without breaking existing ones. 16 | * @skydoves -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | # Allow up to 10 open pull requests for pip dependencies 13 | open-pull-requests-limit: 10 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### 🎯 Goal 2 | Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. 3 | 4 | ### 🛠 Implementation details 5 | Describe the implementation details for this Pull Request. 6 | 7 | ### ✍️ Explain examples 8 | Explain examples with code for this updates. 9 | 10 | ### Preparing a pull request for review 11 | Ensure your change is properly formatted by running: 12 | 13 | ```gradle 14 | $ ./gradlew spotlessApply 15 | ``` 16 | 17 | Please correct any failures before requesting a review. -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: set up JDK 16 | uses: actions/setup-java@v1 17 | with: 18 | distribution: adopt 19 | java-version: 17 20 | 21 | - name: Cache Gradle and wrapper 22 | uses: actions/cache@v2 23 | with: 24 | path: | 25 | ~/.gradle/caches 26 | ~/.gradle/wrapper 27 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} 28 | restore-keys: | 29 | ${{ runner.os }}-gradle- 30 | 31 | - name: Make Gradle executable 32 | run: chmod +x ./gradlew 33 | 34 | - name: Build with Gradle 35 | run: | 36 | ./gradlew --scan --stacktrace \ 37 | assembleDebug -x :baselineprofile:pixel6api31Setup -x :baselineprofile:pixel6api31NonMinifiedReleaseAndroidTest -x :baselineprofile:collectNonMinifiedReleaseBaselineProfile 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | /.idea 18 | !.idea/codeInsightSettings.xml 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | .idea/workspace.xml 40 | .idea/tasks.xml 41 | .idea/gradle.xml 42 | .idea/dictionaries 43 | .idea/libraries 44 | app/.idea/ 45 | 46 | # Mac 47 | *.DS_Store 48 | 49 | # Keystore files 50 | *.jks 51 | 52 | # External native build folder generated in Android Studio 2.2 and later 53 | .externalNativeBuild 54 | 55 | # Google Services (e.g. APIs or Firebase) 56 | google-services.json 57 | 58 | # Freeline 59 | freeline.py 60 | freeline/ 61 | freeline_project_description.json 62 | 63 | # Eclipse 64 | .project 65 | .settings 66 | .classpath 67 | 68 | # Kotlin 69 | .kotlin -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute 2 | We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. 3 | 4 | ## Preparing a pull request for review 5 | Ensure your change is properly formatted by running: 6 | 7 | ```gradle 8 | ./gradlew spotlessApply 9 | ``` 10 | 11 | Please correct any failures before requesting a review. 12 | 13 | ## Code reviews 14 | All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) for more information on using pull requests. 15 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: skydoves 2 | custom: ["https://www.paypal.me/skydoves", "https://www.buymeacoffee.com/skydoves"] 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/benchmark-rules.pro: -------------------------------------------------------------------------------- 1 | -dontobfuscate 2 | -dontwarn com.google.errorprone.annotations.InlineMe 3 | -------------------------------------------------------------------------------- /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.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 -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 20 | 21 | 22 | 23 | 31 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 48 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/MainActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone 18 | 19 | import android.os.Bundle 20 | import androidx.activity.ComponentActivity 21 | import androidx.activity.compose.setContent 22 | import dagger.hilt.android.AndroidEntryPoint 23 | import io.getstream.chat.android.client.ChatClient 24 | import io.getstream.chat.android.twitchclone.ui.TwitchCloneMain 25 | 26 | @AndroidEntryPoint 27 | class MainActivity : ComponentActivity() { 28 | 29 | override fun onCreate(savedInstanceState: Bundle?) { 30 | super.onCreate(savedInstanceState) 31 | 32 | setContent { TwitchCloneMain() } 33 | } 34 | 35 | override fun onDestroy() { 36 | super.onDestroy() 37 | 38 | ChatClient.instance().disconnect(false).enqueue() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/TwitchCloneApp.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone 18 | 19 | import android.app.Application 20 | import dagger.hilt.android.HiltAndroidApp 21 | 22 | @HiltAndroidApp 23 | class TwitchCloneApp : Application() 24 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/initializer/AssetsInitializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.initializer 18 | 19 | import android.content.Context 20 | import androidx.startup.Initializer 21 | import io.getstream.chat.android.twitchclone.designsystem.assets.AssetsProvider 22 | import io.getstream.log.streamLog 23 | 24 | class AssetsInitializer : Initializer { 25 | 26 | override fun create(context: Context) { 27 | streamLog { "AssetsInitializer is initialized" } 28 | 29 | AssetsProvider.loadBadges(context) 30 | AssetsProvider.loadReactions(context) 31 | } 32 | 33 | override fun dependencies(): List>> = emptyList() 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/initializer/MainInitializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.initializer 18 | 19 | import android.content.Context 20 | import androidx.startup.Initializer 21 | 22 | class MainInitializer : Initializer { 23 | 24 | override fun create(context: Context) = Unit 25 | 26 | override fun dependencies(): List>> = listOf( 27 | StreamLogInitializer::class.java, 28 | StreamChatInitializer::class.java, 29 | StreamVideoInitializer::class.java, 30 | AssetsInitializer::class.java 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/initializer/StreamChatInitializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.initializer 18 | 19 | import android.content.Context 20 | import androidx.startup.Initializer 21 | import io.getstream.chat.android.client.ChatClient 22 | import io.getstream.chat.android.client.logger.ChatLogLevel 23 | import io.getstream.chat.android.models.User 24 | import io.getstream.chat.android.offline.plugin.factory.StreamOfflinePluginFactory 25 | import io.getstream.chat.android.state.plugin.config.StatePluginConfig 26 | import io.getstream.chat.android.state.plugin.factory.StreamStatePluginFactory 27 | import io.getstream.chat.android.twitchclone.BuildConfig 28 | import io.getstream.log.streamLog 29 | 30 | /** 31 | * StreamChatInitializer initializes all Stream Client components. 32 | */ 33 | class StreamChatInitializer : Initializer { 34 | 35 | override fun create(context: Context) { 36 | streamLog { "StreamChatInitializer is initialized" } 37 | 38 | /** 39 | * initialize a global instance of the [ChatClient]. 40 | * The ChatClient is the main entry point for all low-level operations on chat. 41 | * e.g, connect/disconnect user to the server, send/update/pin message, etc. 42 | */ 43 | val logLevel = if (BuildConfig.DEBUG) ChatLogLevel.ALL else ChatLogLevel.NOTHING 44 | val offlinePluginFactory = StreamOfflinePluginFactory( 45 | appContext = context 46 | ) 47 | val statePluginFactory = StreamStatePluginFactory( 48 | config = StatePluginConfig( 49 | backgroundSyncEnabled = true, 50 | userPresence = true 51 | ), 52 | appContext = context 53 | ) 54 | val chatClient = ChatClient.Builder(BuildConfig.STREAM_API_KEY, context) 55 | .withPlugins(offlinePluginFactory, statePluginFactory) 56 | .logLevel(logLevel) 57 | .build() 58 | 59 | val user = User( 60 | id = "jaewoong12", 61 | name = "Jaewoong" 62 | ) 63 | 64 | val token = chatClient.devToken(user.id) 65 | chatClient.connectUser(user, token).enqueue() 66 | } 67 | 68 | override fun dependencies(): List>> = 69 | listOf(StreamLogInitializer::class.java) 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/initializer/StreamLogInitializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.initializer 18 | 19 | import android.content.Context 20 | import androidx.startup.Initializer 21 | import io.getstream.log.Priority 22 | import io.getstream.log.android.AndroidStreamLogger 23 | import io.getstream.log.android.BuildConfig 24 | import io.getstream.log.streamLog 25 | 26 | class StreamLogInitializer : Initializer { 27 | 28 | override fun create(context: Context) { 29 | if (BuildConfig.DEBUG) { 30 | AndroidStreamLogger.install(minPriority = Priority.DEBUG) 31 | streamLog { "StreamLogInitializer is initialized" } 32 | } 33 | } 34 | 35 | override fun dependencies(): List>> = emptyList() 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/initializer/StreamVideoInitializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.initializer 18 | 19 | import android.content.Context 20 | import androidx.startup.Initializer 21 | import io.getstream.chat.android.twitchclone.BuildConfig 22 | import io.getstream.log.streamLog 23 | import io.getstream.video.android.core.StreamVideo 24 | import io.getstream.video.android.core.StreamVideoBuilder 25 | import io.getstream.video.android.core.notifications.NotificationConfig 26 | import io.getstream.video.android.model.User 27 | 28 | class StreamVideoInitializer : Initializer { 29 | 30 | override fun create(context: Context) { 31 | streamLog { "StreamVideoInitializer is initialized" } 32 | 33 | val userId = "jaewoong" 34 | StreamVideoBuilder( 35 | context = context, 36 | apiKey = BuildConfig.STREAM_API_KEY, 37 | notificationConfig = NotificationConfig(hideRingingNotificationInForeground = true), 38 | runForegroundServiceForCalls = false, 39 | token = StreamVideo.devToken(userId), 40 | user = User( 41 | id = userId, 42 | name = "Jaewoong", 43 | image = "http://placekitten.com/200/300", 44 | role = "admin" 45 | ) 46 | ).build() 47 | } 48 | 49 | override fun dependencies(): List>> = 50 | listOf(StreamLogInitializer::class.java) 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/navigation/NavSlideComposable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.navigation 18 | 19 | import androidx.compose.animation.AnimatedContentScope 20 | import androidx.compose.animation.AnimatedContentTransitionScope 21 | import androidx.compose.animation.core.tween 22 | import androidx.compose.runtime.Composable 23 | import androidx.navigation.NamedNavArgument 24 | import androidx.navigation.NavBackStackEntry 25 | import androidx.navigation.NavDeepLink 26 | import androidx.navigation.NavGraphBuilder 27 | import androidx.navigation.compose.composable 28 | 29 | fun NavGraphBuilder.slideComposable( 30 | route: String, 31 | arguments: List = emptyList(), 32 | deepLinks: List = emptyList(), 33 | content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit 34 | ) { 35 | composable( 36 | route = route, 37 | enterTransition = { 38 | slideIntoContainer( 39 | towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left, 40 | animationSpec = tween(500) 41 | ) 42 | }, 43 | exitTransition = { 44 | slideOutOfContainer( 45 | towards = AnimatedContentTransitionScope.SlideDirection.Companion.Left, 46 | animationSpec = tween(500) 47 | ) 48 | }, 49 | popEnterTransition = { 50 | slideIntoContainer( 51 | towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, 52 | animationSpec = tween(500) 53 | ) 54 | }, 55 | popExitTransition = { 56 | slideOutOfContainer( 57 | towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, 58 | animationSpec = tween(500) 59 | ) 60 | }, 61 | arguments = arguments, 62 | deepLinks = deepLinks, 63 | content = content 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/navigation/TwitchNavHost.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.navigation 18 | 19 | import androidx.compose.runtime.Composable 20 | import androidx.navigation.NavHostController 21 | import androidx.navigation.compose.NavHost 22 | 23 | @Composable 24 | fun TwitchNavHost( 25 | navHostController: NavHostController 26 | ) { 27 | NavHost( 28 | navController = navHostController, 29 | startDestination = TwitchScreens.Channels.route 30 | ) { 31 | twitchAppHomeNavigation() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/kotlin/io/getstream/chat/android/twitchclone/ui/TwitchCloneMain.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.ui 18 | 19 | import android.os.Build 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.runtime.CompositionLocalProvider 22 | import androidx.compose.runtime.LaunchedEffect 23 | import androidx.compose.runtime.remember 24 | import androidx.compose.ui.platform.LocalContext 25 | import androidx.navigation.compose.rememberNavController 26 | import coil.ImageLoader 27 | import coil.decode.GifDecoder 28 | import coil.decode.ImageDecoderDecoder 29 | import com.skydoves.landscapist.coil.LocalCoilImageLoader 30 | import io.getstream.chat.android.twitchclone.designsystem.TwitchTheme 31 | import io.getstream.chat.android.twitchclone.navigation.LocalComposeNavigator 32 | import io.getstream.chat.android.twitchclone.navigation.TwitchCloneComposeNavigator 33 | import io.getstream.chat.android.twitchclone.navigation.TwitchNavHost 34 | 35 | @Composable 36 | fun TwitchCloneMain() { 37 | val context = LocalContext.current 38 | val imageLoader = remember { 39 | ImageLoader.Builder(context) 40 | .components { 41 | if (Build.VERSION.SDK_INT >= 28) { 42 | add(ImageDecoderDecoder.Factory()) 43 | } else { 44 | add(GifDecoder.Factory()) 45 | } 46 | } 47 | .build() 48 | } 49 | val appComposeNavigator = remember { TwitchCloneComposeNavigator() } 50 | 51 | CompositionLocalProvider( 52 | LocalCoilImageLoader provides imageLoader, 53 | LocalComposeNavigator provides appComposeNavigator 54 | ) { 55 | TwitchTheme { 56 | val navHostController = rememberNavController() 57 | 58 | LaunchedEffect(Unit) { 59 | appComposeNavigator.handleNavigationCommands(navHostController) 60 | } 61 | 62 | TwitchNavHost( 63 | navHostController = navHostController 64 | ) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /baselineprofile/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /baselineprofile/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.getstream.chat.android.Configuration 2 | 3 | @Suppress("DSL_SCOPE_VIOLATION") 4 | plugins { 5 | id(libs.plugins.android.test.get().pluginId) 6 | id(libs.plugins.kotlin.android.get().pluginId) 7 | id(libs.plugins.baseline.profile.get().pluginId) 8 | } 9 | 10 | android { 11 | namespace = "io.getstream.chat.android.twitchclone.baselineprofile" 12 | compileSdk = Configuration.compileSdk 13 | 14 | compileOptions { 15 | sourceCompatibility = JavaVersion.VERSION_17 16 | targetCompatibility = JavaVersion.VERSION_17 17 | } 18 | 19 | kotlinOptions { 20 | jvmTarget = libs.versions.jvmTarget.get() 21 | } 22 | 23 | defaultConfig { 24 | minSdk = 24 25 | targetSdk = Configuration.targetSdk 26 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 27 | } 28 | 29 | buildTypes { 30 | create("benchmark") { 31 | isDebuggable = false 32 | signingConfig = signingConfigs.getByName("debug") 33 | matchingFallbacks += listOf("release") 34 | } 35 | } 36 | 37 | targetProjectPath = ":app" 38 | 39 | testOptions.managedDevices.devices { 40 | maybeCreate("pixel6api31").apply { 41 | device = "Pixel 6" 42 | apiLevel = 31 43 | systemImageSource = "aosp" 44 | } 45 | } 46 | } 47 | 48 | // This is the plugin configuration. Everything is optional. Defaults are in the 49 | // comments. In this example, you use the GMD added earlier and disable connected devices. 50 | baselineProfile { 51 | 52 | // This specifies the managed devices to use that you run the tests on. The default 53 | // is none. 54 | managedDevices += "pixel6api31" 55 | 56 | // This enables using connected devices to generate profiles. The default is true. 57 | // When using connected devices, they must be rooted or API 33 and higher. 58 | useConnectedDevices = false 59 | } 60 | 61 | dependencies { 62 | implementation(libs.androidx.test.runner) 63 | implementation(libs.androidx.test.uiautomator) 64 | implementation(libs.androidx.benchmark.macro) 65 | implementation(libs.androidx.profileinstaller) 66 | } 67 | -------------------------------------------------------------------------------- /baselineprofile/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /baselineprofile/src/main/kotlin/io/getstream/chat/android/twitchclone/baselineprofile/BaselineProfileGenerator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Designed and developed by 2023 skydoves (Jaewoong Eum) 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 | * http://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 | package io.getstream.chat.android.twitchclone.baselineprofile 17 | 18 | import android.os.Build 19 | import androidx.annotation.RequiresApi 20 | import androidx.benchmark.macro.junit4.BaselineProfileRule 21 | import org.junit.Rule 22 | import org.junit.Test 23 | 24 | @RequiresApi(Build.VERSION_CODES.P) 25 | class BaselineProfileGenerator { 26 | @get:Rule 27 | val baselineProfileRule = BaselineProfileRule() 28 | 29 | @Test 30 | fun startup() = 31 | baselineProfileRule.collect( 32 | packageName = "io.getstream.chat.android.twitchclone", 33 | includeInStartupProfile = true, 34 | stableIterations = 2, 35 | maxIterations = 8, 36 | ) { 37 | pressHome() 38 | // This block defines the app's critical user journey. Here we are interested in 39 | // optimizing for app startup. But you can also navigate and scroll 40 | // through your most important UI. 41 | startActivityAndWait() 42 | device.waitForIdle() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /build-logic/convention/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | group = "io.getstream.chat.android.twitchclone.buildlogic" 6 | 7 | java { 8 | sourceCompatibility = JavaVersion.VERSION_17 9 | targetCompatibility = JavaVersion.VERSION_17 10 | } 11 | 12 | dependencies { 13 | compileOnly(libs.android.gradlePlugin) 14 | compileOnly(libs.kotlin.gradlePlugin) 15 | compileOnly(libs.compose.compiler.gradlePlugin) 16 | compileOnly(libs.spotless.gradlePlugin) 17 | } 18 | 19 | gradlePlugin { 20 | plugins { 21 | register("androidApplicationCompose") { 22 | id = "getstream.android.application.compose" 23 | implementationClass = "AndroidApplicationComposeConventionPlugin" 24 | } 25 | register("androidApplication") { 26 | id = "getstream.android.application" 27 | implementationClass = "AndroidApplicationConventionPlugin" 28 | } 29 | register("androidLibraryCompose") { 30 | id = "getstream.android.library.compose" 31 | implementationClass = "AndroidLibraryComposeConventionPlugin" 32 | } 33 | register("androidLibrary") { 34 | id = "getstream.android.library" 35 | implementationClass = "AndroidLibraryConventionPlugin" 36 | } 37 | register("androidFeature") { 38 | id = "getstream.android.feature" 39 | implementationClass = "AndroidFeatureConventionPlugin" 40 | } 41 | register("androidHilt") { 42 | id = "getstream.android.hilt" 43 | implementationClass = "AndroidHiltConventionPlugin" 44 | } 45 | register("spotless") { 46 | id = "getstream.spotless" 47 | implementationClass = "SpotlessConventionPlugin" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.internal.dsl.BaseAppModuleExtension 2 | import io.getstream.chat.android.twitchclone.configureAndroidCompose 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.kotlin.dsl.getByType 6 | 7 | class AndroidApplicationComposeConventionPlugin : Plugin { 8 | override fun apply(target: Project) { 9 | with(target) { 10 | pluginManager.apply("com.android.application") 11 | val extension = extensions.getByType() 12 | configureAndroidCompose(extension) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.internal.dsl.BaseAppModuleExtension 2 | import io.getstream.chat.android.twitchclone.configureKotlinAndroid 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.kotlin.dsl.configure 6 | 7 | class AndroidApplicationConventionPlugin : Plugin { 8 | override fun apply(target: Project) { 9 | with(target) { 10 | with(pluginManager) { 11 | apply("com.android.application") 12 | apply("org.jetbrains.kotlin.android") 13 | } 14 | 15 | extensions.configure { 16 | configureKotlinAndroid(this) 17 | defaultConfig.targetSdk = 34 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Plugin 2 | import org.gradle.api.Project 3 | import org.gradle.api.artifacts.VersionCatalogsExtension 4 | import org.gradle.kotlin.dsl.dependencies 5 | import org.gradle.kotlin.dsl.getByType 6 | 7 | class AndroidFeatureConventionPlugin : Plugin { 8 | override fun apply(target: Project) { 9 | with(target) { 10 | pluginManager.apply { 11 | apply("com.android.library") 12 | apply("org.jetbrains.kotlin.android") 13 | } 14 | 15 | val libs = extensions.getByType().named("libs") 16 | 17 | dependencies { 18 | add("implementation", project(":core-designsystem")) 19 | add("implementation", project(":core-navigation")) 20 | add("implementation", project(":core-data")) 21 | 22 | add("implementation", libs.findLibrary("kotlinx.coroutines.android").get()) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.Plugin 2 | import org.gradle.api.Project 3 | import org.gradle.api.artifacts.VersionCatalogsExtension 4 | import org.gradle.kotlin.dsl.dependencies 5 | import org.gradle.kotlin.dsl.getByType 6 | 7 | class AndroidHiltConventionPlugin : Plugin { 8 | override fun apply(target: Project) { 9 | with(target) { 10 | with(pluginManager) { 11 | apply("dagger.hilt.android.plugin") 12 | apply("com.google.devtools.ksp") 13 | } 14 | 15 | val libs = extensions.getByType().named("libs") 16 | 17 | dependencies { 18 | add("implementation", libs.findLibrary("hilt.android").get()) 19 | add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) 20 | add("ksp", libs.findLibrary("hilt.compiler").get()) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.LibraryExtension 2 | import io.getstream.chat.android.twitchclone.configureAndroidCompose 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.kotlin.dsl.getByType 6 | 7 | class AndroidLibraryComposeConventionPlugin : Plugin { 8 | override fun apply(target: Project) { 9 | with(target) { 10 | pluginManager.apply("com.android.library") 11 | val extension = extensions.getByType() 12 | configureAndroidCompose(extension) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.LibraryExtension 2 | import io.getstream.chat.android.twitchclone.configureKotlinAndroid 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.kotlin.dsl.configure 6 | 7 | class AndroidLibraryConventionPlugin : Plugin { 8 | override fun apply(target: Project) { 9 | with(target) { 10 | with(pluginManager) { 11 | apply("com.android.library") 12 | apply("org.jetbrains.kotlin.android") 13 | } 14 | 15 | extensions.configure { 16 | configureKotlinAndroid(this) 17 | defaultConfig.targetSdk = 34 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | import com.diffplug.gradle.spotless.SpotlessExtension 2 | import org.gradle.api.Plugin 3 | import org.gradle.api.Project 4 | import org.gradle.kotlin.dsl.configure 5 | 6 | class SpotlessConventionPlugin : Plugin { 7 | override fun apply(target: Project) { 8 | with(target) { 9 | pluginManager.apply("com.diffplug.spotless") 10 | 11 | extensions.configure { 12 | kotlin { 13 | target("**/*.kt") 14 | targetExclude("**/build/**/*.kt") 15 | ktlint() 16 | .setUseExperimental(true) 17 | .userData(mapOf("android" to "true")) 18 | .editorConfigOverride(mapOf("indent_size" to 2, "continuation_indent_size" to 2)) 19 | licenseHeaderFile(rootProject.file("$rootDir/spotless/copyright.kt")) 20 | } 21 | format("kts") { 22 | target("**/*.kts") 23 | targetExclude("**/build/**/*.kts") 24 | // Look for the first line that doesn't have a block comment (assumed to be the license) 25 | licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") 26 | } 27 | format("xml") { 28 | target("**/*.xml") 29 | targetExclude("**/build/**/*.xml") 30 | // Look for the first XML tag that isn't a comment ( 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /core-data/src/main/kotlin/io/getstream/chat/android/twitchclone/data/coroutines/WhileSubscribedOrRetained.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.data.coroutines 18 | 19 | import android.os.Handler 20 | import android.os.Looper 21 | import android.view.Choreographer 22 | import kotlinx.coroutines.CompletableDeferred 23 | import kotlinx.coroutines.flow.Flow 24 | import kotlinx.coroutines.flow.SharingCommand 25 | import kotlinx.coroutines.flow.SharingStarted 26 | import kotlinx.coroutines.flow.StateFlow 27 | import kotlinx.coroutines.flow.distinctUntilChanged 28 | import kotlinx.coroutines.flow.dropWhile 29 | import kotlinx.coroutines.flow.transformLatest 30 | 31 | /** 32 | * Sharing is started when the first subscriber appears, 33 | * immediately stops when the last subscriber disappears (by default), 34 | * keeping the replay cache forever (by default) even if configuration changes happen. 35 | * 36 | * https://py.hashnode.dev/whilesubscribed5000 37 | */ 38 | data object WhileSubscribedOrRetained : SharingStarted { 39 | 40 | private val handler = Handler(Looper.getMainLooper()) 41 | 42 | override fun command(subscriptionCount: StateFlow): Flow = subscriptionCount 43 | .transformLatest { count -> 44 | if (count > 0) { 45 | emit(SharingCommand.START) 46 | } else { 47 | val posted = CompletableDeferred() 48 | // This code is perfect. Do not change a thing. 49 | Choreographer.getInstance().postFrameCallback { 50 | handler.postAtFrontOfQueue { 51 | handler.post { 52 | posted.complete(Unit) 53 | } 54 | } 55 | } 56 | posted.await() 57 | emit(SharingCommand.STOP) 58 | } 59 | } 60 | .dropWhile { it != SharingCommand.START } 61 | .distinctUntilChanged() 62 | 63 | override fun toString(): String = "WhileSubscribedOrRetained" 64 | } 65 | -------------------------------------------------------------------------------- /core-data/src/main/kotlin/io/getstream/chat/android/twitchclone/data/di/DataModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.data.di 18 | 19 | import dagger.Binds 20 | import dagger.Module 21 | import dagger.hilt.InstallIn 22 | import dagger.hilt.components.SingletonComponent 23 | import io.getstream.chat.android.twitchclone.data.repository.RewardRepository 24 | import io.getstream.chat.android.twitchclone.data.repository.RewardRepositoryImpl 25 | 26 | @Module 27 | @InstallIn(SingletonComponent::class) 28 | internal interface DataModule { 29 | 30 | @Binds 31 | fun bindsCallHistoryRepository( 32 | rewardRepositoryImpl: RewardRepositoryImpl 33 | ): RewardRepository 34 | } 35 | -------------------------------------------------------------------------------- /core-data/src/main/kotlin/io/getstream/chat/android/twitchclone/data/mapper/Mappers.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.data.mapper 18 | 19 | import io.getstream.chat.android.twitchclone.database.entity.RewardEntity 20 | import io.getstream.chat.android.twitchclone.model.Reward 21 | 22 | fun RewardEntity.toModel(): Reward { 23 | return Reward(name, color, icon, tokenAmount) 24 | } 25 | -------------------------------------------------------------------------------- /core-data/src/main/kotlin/io/getstream/chat/android/twitchclone/data/repository/RewardRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.data.repository 18 | 19 | import com.skydoves.sandwich.ApiResponse 20 | import io.getstream.chat.android.twitchclone.model.Reward 21 | import kotlinx.coroutines.flow.Flow 22 | 23 | interface RewardRepository { 24 | 25 | fun getRewardStream(): Flow>> 26 | 27 | suspend fun updateRewards(rewards: ApiResponse>) 28 | } 29 | -------------------------------------------------------------------------------- /core-data/src/main/kotlin/io/getstream/chat/android/twitchclone/data/repository/RewardRepositoryImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.data.repository 18 | 19 | import com.skydoves.sandwich.ApiResponse 20 | import com.skydoves.sandwich.getOrNull 21 | import io.getstream.chat.android.twitchclone.data.mapper.toModel 22 | import io.getstream.chat.android.twitchclone.database.dao.RewardDao 23 | import io.getstream.chat.android.twitchclone.database.entity.asEntity 24 | import io.getstream.chat.android.twitchclone.model.Reward 25 | import io.getstream.chat.android.twitchclone.network.Dispatcher 26 | import io.getstream.chat.android.twitchclone.network.TwitchDispatchers 27 | import io.getstream.chat.android.twitchclone.network.service.RewardsService 28 | import javax.inject.Inject 29 | import kotlinx.coroutines.CoroutineDispatcher 30 | import kotlinx.coroutines.flow.Flow 31 | import kotlinx.coroutines.flow.flow 32 | import kotlinx.coroutines.flow.flowOn 33 | 34 | /** 35 | * This is an implementation of [RewardRepository]. 36 | * 37 | * Make sure the implementation class must not be exposed to the outside by using 'internal' visibility modifier. 38 | */ 39 | internal class RewardRepositoryImpl @Inject constructor( 40 | @Dispatcher(TwitchDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, 41 | private val rewardsService: RewardsService, 42 | private val rewardDao: RewardDao 43 | ) : RewardRepository { 44 | 45 | override fun getRewardStream(): Flow>> = flow { 46 | val offlineRewards = rewardDao.getRewards() 47 | if (offlineRewards.isEmpty()) { 48 | val apiResponse = rewardsService.fetchRewards() 49 | updateRewards(apiResponse) 50 | emit(apiResponse) 51 | } else { 52 | emit(ApiResponse.Success(offlineRewards.map { it.toModel() })) 53 | } 54 | }.flowOn(ioDispatcher) 55 | 56 | override suspend fun updateRewards(rewards: ApiResponse>) { 57 | val entities = rewards.getOrNull()?.map { it.asEntity() } ?: return 58 | rewardDao.insertRewards(entities) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core-database/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core-database/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | plugins { 17 | id("getstream.android.library") 18 | id("getstream.android.hilt") 19 | id("getstream.spotless") 20 | id("com.google.devtools.ksp") 21 | } 22 | 23 | android { 24 | namespace = "io.getstream.chat.android.twitchclone.database" 25 | defaultConfig { 26 | // The schemas directory contains a schema file for each version of the Room database. 27 | // This is required to enable Room auto migrations. 28 | // See https://developer.android.com/reference/kotlin/androidx/room/AutoMigration. 29 | ksp { 30 | arg("room.schemaLocation", "$projectDir/schemas") 31 | } 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation(project(":core-model")) 37 | 38 | implementation(libs.androidx.room.runtime) 39 | implementation(libs.androidx.room.ktx) 40 | ksp(libs.androidx.room.compiler) 41 | } 42 | -------------------------------------------------------------------------------- /core-database/schemas/io.getstream.chat.android.twitchclone.database.TwitchDatabase/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "c24a63fb1161cd6e8a14736c19dc2aec", 6 | "entities": [ 7 | { 8 | "tableName": "reward", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `color` TEXT NOT NULL, `icon` TEXT NOT NULL, `tokenAmount` INTEGER NOT NULL, PRIMARY KEY(`name`))", 10 | "fields": [ 11 | { 12 | "fieldPath": "name", 13 | "columnName": "name", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "color", 19 | "columnName": "color", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "icon", 25 | "columnName": "icon", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "tokenAmount", 31 | "columnName": "tokenAmount", 32 | "affinity": "INTEGER", 33 | "notNull": true 34 | } 35 | ], 36 | "primaryKey": { 37 | "autoGenerate": false, 38 | "columnNames": [ 39 | "name" 40 | ] 41 | }, 42 | "indices": [], 43 | "foreignKeys": [] 44 | } 45 | ], 46 | "views": [], 47 | "setupQueries": [ 48 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 49 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c24a63fb1161cd6e8a14736c19dc2aec')" 50 | ] 51 | } 52 | } -------------------------------------------------------------------------------- /core-database/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /core-database/src/main/kotlin/io/getstream/chat/android/twitchclone/database/TwitchDatabase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.database 18 | 19 | import androidx.room.Database 20 | import androidx.room.RoomDatabase 21 | import io.getstream.chat.android.twitchclone.database.dao.RewardDao 22 | import io.getstream.chat.android.twitchclone.database.entity.RewardEntity 23 | 24 | @Database( 25 | entities = [RewardEntity::class], 26 | version = 1, 27 | exportSchema = true 28 | ) 29 | internal abstract class TwitchDatabase : RoomDatabase() { 30 | abstract fun rewardDao(): RewardDao 31 | } 32 | -------------------------------------------------------------------------------- /core-database/src/main/kotlin/io/getstream/chat/android/twitchclone/database/dao/RewardDao.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.database.dao 18 | 19 | import androidx.room.Dao 20 | import androidx.room.Insert 21 | import androidx.room.OnConflictStrategy 22 | import androidx.room.Query 23 | import io.getstream.chat.android.twitchclone.database.entity.RewardEntity 24 | 25 | @Dao 26 | interface RewardDao { 27 | 28 | @Query(value = "SELECT * FROM reward") 29 | suspend fun getRewards(): List 30 | 31 | @Insert(onConflict = OnConflictStrategy.REPLACE) 32 | suspend fun insertRewards(rewardEntity: List) 33 | } 34 | -------------------------------------------------------------------------------- /core-database/src/main/kotlin/io/getstream/chat/android/twitchclone/database/di/DatabaseModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.database.di 18 | 19 | import android.content.Context 20 | import androidx.room.Room 21 | import dagger.Module 22 | import dagger.Provides 23 | import dagger.hilt.InstallIn 24 | import dagger.hilt.android.qualifiers.ApplicationContext 25 | import dagger.hilt.components.SingletonComponent 26 | import io.getstream.chat.android.twitchclone.database.TwitchDatabase 27 | import io.getstream.chat.android.twitchclone.database.dao.RewardDao 28 | import javax.inject.Singleton 29 | 30 | @Module 31 | @InstallIn(SingletonComponent::class) 32 | internal object DatabaseModule { 33 | 34 | @Provides 35 | @Singleton 36 | fun provideTwitchCloneDatabase( 37 | @ApplicationContext context: Context 38 | ): TwitchDatabase = Room.databaseBuilder( 39 | context, 40 | TwitchDatabase::class.java, 41 | "twitchclone-database" 42 | ).build() 43 | 44 | @Provides 45 | @Singleton 46 | fun provideTwitchUserDao( 47 | twitchDatabase: TwitchDatabase 48 | ): RewardDao { 49 | return twitchDatabase.rewardDao() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core-database/src/main/kotlin/io/getstream/chat/android/twitchclone/database/entity/RewardEntity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.database.entity 18 | 19 | import androidx.room.Entity 20 | import androidx.room.PrimaryKey 21 | import io.getstream.chat.android.twitchclone.model.Reward 22 | 23 | @Entity(tableName = "reward") 24 | data class RewardEntity( 25 | @PrimaryKey val name: String, 26 | val color: String, 27 | val icon: String, 28 | val tokenAmount: Int 29 | ) 30 | 31 | fun Reward.asEntity(): RewardEntity { 32 | return RewardEntity( 33 | name = name, 34 | color = color, 35 | icon = icon, 36 | tokenAmount = tokenAmount 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /core-designsystem/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core-designsystem/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | plugins { 17 | id("getstream.android.library") 18 | id("getstream.android.library.compose") 19 | id("getstream.spotless") 20 | } 21 | 22 | android { 23 | namespace = "io.getstream.chat.android.twitchclone.designsystem" 24 | } 25 | 26 | dependencies { 27 | api(libs.androidx.startup) 28 | 29 | // Stream SDK 30 | implementation(libs.stream.chat.compose) 31 | implementation(libs.stream.video.compose) 32 | 33 | // image loading 34 | api(libs.landscapist.coil) 35 | api(libs.landscapist.animation) 36 | api(libs.landscapist.placeholder) 37 | 38 | // compose 39 | api(libs.androidx.compose.runtime) 40 | api(libs.androidx.compose.ui) 41 | api(libs.androidx.compose.ui.tooling) 42 | api(libs.androidx.compose.ui.tooling.preview) 43 | api(libs.androidx.compose.material.iconsExtended) 44 | api(libs.androidx.compose.material) 45 | api(libs.androidx.compose.foundation) 46 | api(libs.androidx.compose.foundation.layout) 47 | api(libs.androidx.compose.constraintlayout) 48 | } 49 | -------------------------------------------------------------------------------- /core-designsystem/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/badges/cat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/badges/cat.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/badges/cat2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/badges/cat2.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/badges/cool.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/badges/cool.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/badges/gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/badges/gold.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/badges/prime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/badges/prime.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/badges/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/badges/star.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/angelthump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/angelthump.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/ariw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/ariw.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/brobalt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/brobalt.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/bttvnice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/bttvnice.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/burself.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/burself.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/candianrage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/candianrage.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/cigrip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/cigrip.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/concerndoge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/concerndoge.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/cruw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/cruw.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/cvhazmat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/cvhazmat.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/cvl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/cvl.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/cvmask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/cvmask.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/cvr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/cvr.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/datsauce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/datsauce.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/dogchamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/dogchamp.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/duckerz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/duckerz.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/feelsamazingman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/feelsamazingman.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/feelsbadman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/feelsbadman.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/feelsbirthdayman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/feelsbirthdayman.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/feelsgoodman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/feelsgoodman.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/feelssnowman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/feelssnowman.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/feelssnowyman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/feelssnowyman.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/firespeed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/firespeed.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/fishmoley.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/fishmoley.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/foreveralone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/foreveralone.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/gaben.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/gaben.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/hahaa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/hahaa.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/hailhelix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/hailhelix.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/hhhehehe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/hhhehehe.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/icecold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/icecold.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/kappacool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/kappacool.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/karappa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/karappa.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/kkona.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/kkona.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/lul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/lul.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/m&mjc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/m&mjc.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/monkas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/monkas.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/nam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/nam.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/notsquishy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/notsquishy.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/poledoge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/poledoge.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/rarepepe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/rarepepe.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/ronsmug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/ronsmug.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/saltycorn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/saltycorn.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/shoopdawhoop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/shoopdawhoop.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/sosgame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/sosgame.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/sqshy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/sqshy.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/taxibro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/taxibro.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/tehpolecat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/tehpolecat.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/twat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/twat.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/vapenation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/vapenation.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/vislaud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/vislaud.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/watchusay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/watchusay.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/wowee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/wowee.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/emotes/wubtf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/emotes/wubtf.png -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/aaugh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/aaugh.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/ayayajam.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/ayayajam.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/balddab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/balddab.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/baldflick.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/baldflick.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/baldfloss.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/baldfloss.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/baldspin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/baldspin.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/baldyappp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/baldyappp.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/baldyikes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/baldyikes.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/baners.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/baners.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/bropls.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/bropls.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/catjam.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/catjam.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/checkers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/checkers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/click.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/click.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/coomtime.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/coomtime.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/crawlers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/crawlers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/deadlole.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/deadlole.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/deskchan.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/deskchan.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/eato.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/eato.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/eddiebaldmansmash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/eddiebaldmansmash.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/eddieknead.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/eddieknead.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/eddiespin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/eddiespin.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/eekum.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/eekum.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/flappers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/flappers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/fubaldi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/fubaldi.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/gamba.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/gamba.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/gawkgawk.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/gawkgawk.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/guitartime.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/guitartime.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/humpers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/humpers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/hypernodders.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/hypernodders.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/hypernopers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/hypernopers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/hyperpeepod.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/hyperpeepod.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/johnsouls.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/johnsouls.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/kissabrother.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/kissabrother.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/kissapregomie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/kissapregomie.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/komodochomp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/komodochomp.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/lgiggle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/lgiggle.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/mariorun.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/mariorun.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/moon2bass.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/moon2bass.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/noted.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/noted.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peepeegachat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peepeegachat.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peepees.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peepees.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peepersd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peepersd.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peepoboom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peepoboom.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peepocheering.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peepocheering.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peepogolfclap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peepogolfclap.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peeponarusprint.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peeponarusprint.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peeposhy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peeposhy.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/peeposteer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/peeposteer.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/pepelepsy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/pepelepsy.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/pepemetal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/pepemetal.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/petthebaldie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/petthebaldie.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/pettheeddie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/pettheeddie.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/pettheqynoa.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/pettheqynoa.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/pukers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/pukers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/qynoalurks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/qynoalurks.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/raremoon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/raremoon.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/refracting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/refracting.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/robpls.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/robpls.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/shruggers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/shruggers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/shushers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/shushers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/slap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/slap.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/sneakers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/sneakers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/solarflare.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/solarflare.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/soulshroom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/soulshroom.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/speeders.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/speeders.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/tanties.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/tanties.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/teatime.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/teatime.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/teatime2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/teatime2.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/tinyteeth.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/tinyteeth.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/twerkers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/twerkers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/vanish.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/vanish.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/vibers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/vibers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/wowers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/wowers.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/assets/gifs/yappp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/assets/gifs/yappp.gif -------------------------------------------------------------------------------- /core-designsystem/src/main/kotlin/io/getstream/chat/android/twitchclone/designsystem/Themes.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.designsystem 18 | 19 | import androidx.compose.foundation.isSystemInDarkTheme 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.ui.graphics.Color 22 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 23 | import io.getstream.chat.android.compose.ui.theme.StreamColors 24 | import io.getstream.video.android.compose.theme.VideoTheme 25 | 26 | val primaryColor = Color(0xFF6441a5) 27 | val shimmerBase: Color = Color(0xFF25282B) 28 | val shimmerHighlight: Color = Color(0xFFDFDEDE) 29 | val emojiColor: Color = Color(0xFFF4BC04) 30 | 31 | @Composable 32 | fun TwitchTheme( 33 | content: @Composable () -> Unit 34 | ) { 35 | ChatTheme( 36 | colors = if (isSystemInDarkTheme()) { 37 | StreamColors.defaultDarkColors().copy( 38 | primaryAccent = primaryColor 39 | ) 40 | } else { 41 | StreamColors.defaultColors().copy( 42 | primaryAccent = primaryColor 43 | ) 44 | } 45 | ) { 46 | VideoTheme(content = content) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core-designsystem/src/main/kotlin/io/getstream/chat/android/twitchclone/designsystem/TwitchLoadingIndicator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.designsystem 18 | 19 | import androidx.compose.foundation.background 20 | import androidx.compose.foundation.layout.Box 21 | import androidx.compose.foundation.layout.fillMaxSize 22 | import androidx.compose.material.CircularProgressIndicator 23 | import androidx.compose.runtime.Composable 24 | import androidx.compose.ui.Alignment 25 | import androidx.compose.ui.Modifier 26 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 27 | 28 | @Composable 29 | public fun TwitchLoadingIndicator() { 30 | Box( 31 | modifier = Modifier 32 | .fillMaxSize() 33 | .background(ChatTheme.colors.appBackground) 34 | ) { 35 | CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core-designsystem/src/main/kotlin/io/getstream/chat/android/twitchclone/designsystem/assets/AssetUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.designsystem.assets 18 | 19 | object AssetUtils { 20 | 21 | const val baseUrl = "file:///android_asset/" 22 | 23 | private const val gifExtension = ".gif" 24 | 25 | private const val gifFolder = "gifs" 26 | 27 | private const val emoteFolder = "emotes" 28 | 29 | fun getEmotePath(emote: String?): String { 30 | val folder = if (emote?.contains(gifExtension) == true) { 31 | gifFolder 32 | } else { 33 | emoteFolder 34 | } 35 | return "$baseUrl$folder/$emote" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core-designsystem/src/main/kotlin/io/getstream/chat/android/twitchclone/designsystem/assets/AssetsProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.designsystem.assets 18 | 19 | import android.content.Context 20 | 21 | object AssetsProvider { 22 | 23 | private val _userBadges = mutableMapOf() 24 | val userBadges: Map = _userBadges 25 | 26 | private val _reactions = mutableMapOf() 27 | val reactions: Map = _reactions 28 | 29 | fun loadBadges(context: Context) { 30 | val assets = context.assets ?: return 31 | val badges = assets.list("badges") as? Array ?: emptyArray() 32 | 33 | _userBadges += badges.map { 34 | val parts = it.split(".") 35 | 36 | parts[0] to it 37 | } 38 | } 39 | 40 | fun loadReactions(context: Context) { 41 | val assets = context.assets ?: return 42 | 43 | val gifs = assets.list("gifs") as? Array ?: emptyArray() 44 | val emotes = assets.list("emotes") as? Array ?: emptyArray() 45 | 46 | _reactions += (gifs + emotes).map { 47 | val parts = it.split(".") 48 | 49 | ":${parts[0]}:" to it 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 24 | 25 | 26 | 33 | 37 | 41 | 42 | 43 | 44 | 51 | 52 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable/ic_emoji.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable/ic_half_moon.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable/ic_heart.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable/ic_sun.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable/ic_vertical_dots.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/drawable/reward_ic.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/core-designsystem/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /core-designsystem/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | #FF000000 19 | #FFFFFFFF 20 | #FF6441a5 21 | 22 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | twitch clone compose 19 | 20 | You are about to give this reward! 21 | Got it! 22 | Follow 23 | 24 | 25 | Reward %s 26 | Give reward %s for %d 27 | Expand emoji reactions 28 | Open chat settings 29 | %1$s\'s Rewards 30 | How to earn %1$s 31 | Followed! 32 | Send a message 33 | Live 34 | Failed to join this livestream channel. 35 | Your livestream is on the backstage now 36 | Video rendering failed 37 | Go Live 38 | Go Backstage 39 | Livestream has been started! 40 | Livestream has been ended. Bye bye~ 41 | 42 | -------------------------------------------------------------------------------- /core-designsystem/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /core-model/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core-model/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | plugins { 17 | id("getstream.android.library") 18 | id("getstream.spotless") 19 | id("kotlin-parcelize") 20 | id("org.jetbrains.kotlin.plugin.serialization") 21 | } 22 | 23 | android { 24 | namespace = "io.getstream.chat.android.twitchclone.model" 25 | } 26 | 27 | dependencies { 28 | api(libs.stream.chat.client) 29 | api(libs.stream.video.core) 30 | 31 | api(libs.retrofit.kotlin.serialization) 32 | api(libs.kotlinx.serialization.json) 33 | compileOnly(libs.compose.stable.marker) 34 | } 35 | -------------------------------------------------------------------------------- /core-model/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /core-model/src/main/kotlin/io/getstream/chat/android/twitchclone/model/Reward.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.model 18 | 19 | import androidx.compose.runtime.Immutable 20 | import kotlinx.serialization.Serializable 21 | 22 | @Immutable 23 | @Serializable 24 | data class Reward( 25 | val name: String, 26 | val color: String, 27 | val icon: String, 28 | val tokenAmount: Int 29 | ) 30 | -------------------------------------------------------------------------------- /core-navigation/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core-navigation/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | plugins { 17 | id("getstream.android.library") 18 | id("getstream.android.library.compose") 19 | id("getstream.android.hilt") 20 | id("getstream.spotless") 21 | } 22 | 23 | android { 24 | namespace = "io.getstream.chat.android.twitchclone.navigation" 25 | } 26 | 27 | dependencies { 28 | implementation(libs.kotlinx.coroutines.android) 29 | api(libs.androidx.navigation.compose) 30 | } 31 | -------------------------------------------------------------------------------- /core-navigation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /core-navigation/src/main/kotlin/io/getstream/chat/android/twitchclone/navigation/LocalNavigation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.navigation 18 | 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.runtime.ProvidableCompositionLocal 21 | import androidx.compose.runtime.ReadOnlyComposable 22 | import androidx.compose.runtime.compositionLocalOf 23 | 24 | public val LocalComposeNavigator: ProvidableCompositionLocal = 25 | compositionLocalOf { 26 | error( 27 | "No AppComposeNavigator provided! " + 28 | "Make sure to wrap all usages of UI components in TwitchTheme." 29 | ) 30 | } 31 | 32 | /** 33 | * Retrieves the current [AppComposeNavigator] at the call site's position in the hierarchy. 34 | */ 35 | public val currentComposeNavigator: AppComposeNavigator 36 | @Composable 37 | @ReadOnlyComposable 38 | get() = LocalComposeNavigator.current 39 | -------------------------------------------------------------------------------- /core-navigation/src/main/kotlin/io/getstream/chat/android/twitchclone/navigation/NavigationCommand.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.navigation 18 | 19 | import androidx.navigation.NavOptions 20 | 21 | sealed class NavigationCommand { 22 | object NavigateUp : NavigationCommand() 23 | } 24 | 25 | sealed class ComposeNavigationCommand : NavigationCommand() { 26 | data class NavigateToRoute(val route: String, val options: NavOptions? = null) : 27 | ComposeNavigationCommand() 28 | 29 | data class NavigateUpWithResult( 30 | val key: String, 31 | val result: T, 32 | val route: String? = null 33 | ) : ComposeNavigationCommand() 34 | 35 | data class PopUpToRoute(val route: String, val inclusive: Boolean) : ComposeNavigationCommand() 36 | } 37 | -------------------------------------------------------------------------------- /core-navigation/src/main/kotlin/io/getstream/chat/android/twitchclone/navigation/TwitchCloneComposeNavigator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.navigation 18 | 19 | import androidx.navigation.NavOptionsBuilder 20 | import androidx.navigation.navOptions 21 | import javax.inject.Inject 22 | 23 | class TwitchCloneComposeNavigator @Inject constructor() : AppComposeNavigator() { 24 | 25 | override fun navigate(route: String, optionsBuilder: (NavOptionsBuilder.() -> Unit)?) { 26 | val options = optionsBuilder?.let { navOptions(it) } 27 | navigationCommands.tryEmit(ComposeNavigationCommand.NavigateToRoute(route, options)) 28 | } 29 | 30 | override fun navigateAndClearBackStack(route: String) { 31 | navigationCommands.tryEmit( 32 | ComposeNavigationCommand.NavigateToRoute( 33 | route, 34 | navOptions { 35 | popUpTo(0) 36 | } 37 | ) 38 | ) 39 | } 40 | 41 | override fun popUpTo(route: String, inclusive: Boolean) { 42 | navigationCommands.tryEmit(ComposeNavigationCommand.PopUpToRoute(route, inclusive)) 43 | } 44 | 45 | override fun navigateBackWithResult( 46 | key: String, 47 | result: T, 48 | route: String? 49 | ) { 50 | navigationCommands.tryEmit( 51 | ComposeNavigationCommand.NavigateUpWithResult( 52 | key = key, 53 | result = result, 54 | route = route 55 | ) 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core-navigation/src/main/kotlin/io/getstream/chat/android/twitchclone/navigation/TwitchScreens.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.navigation 18 | 19 | import androidx.navigation.NamedNavArgument 20 | import androidx.navigation.NavType 21 | import androidx.navigation.navArgument 22 | 23 | sealed class TwitchScreens( 24 | val route: String, 25 | val navArguments: List = emptyList() 26 | ) { 27 | val name: String = route.appendArguments(navArguments) 28 | 29 | // home screen 30 | data object Channels : TwitchScreens("channels") 31 | 32 | // message screen 33 | data object Messages : TwitchScreens( 34 | route = "messages", 35 | navArguments = listOf(navArgument("channelId") { type = NavType.StringType }) 36 | ) { 37 | fun createRoute(channelId: String) = 38 | name.replace("{${navArguments.first().name}}", channelId) 39 | } 40 | 41 | // livestream watcher screen 42 | data object LivestreamWatcher : TwitchScreens( 43 | route = "livestream_watcher", 44 | navArguments = listOf(navArgument("channelId") { type = NavType.StringType }) 45 | ) { 46 | fun createRoute(channelId: String) = 47 | name.replace("{${navArguments.first().name}}", channelId) 48 | } 49 | 50 | // livestream streamer screen 51 | data object LivestreamStreamer : TwitchScreens( 52 | route = "livestream_host", 53 | navArguments = listOf(navArgument("channelId") { type = NavType.StringType }) 54 | ) { 55 | fun createRoute(channelId: String) = 56 | name.replace("{${navArguments.first().name}}", channelId) 57 | } 58 | } 59 | 60 | private fun String.appendArguments(navArguments: List): String { 61 | val mandatoryArguments = navArguments.filter { it.argument.defaultValue == null } 62 | .takeIf { it.isNotEmpty() } 63 | ?.joinToString(separator = "/", prefix = "/") { "{${it.name}}" } 64 | .orEmpty() 65 | val optionalArguments = navArguments.filter { it.argument.defaultValue != null } 66 | .takeIf { it.isNotEmpty() } 67 | ?.joinToString(separator = "&", prefix = "?") { "${it.name}={${it.name}}" } 68 | .orEmpty() 69 | return "$this$mandatoryArguments$optionalArguments" 70 | } 71 | -------------------------------------------------------------------------------- /core-network/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /core-network/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | plugins { 17 | id("getstream.android.library") 18 | id("getstream.android.hilt") 19 | id("getstream.spotless") 20 | id("org.jetbrains.kotlin.plugin.serialization") 21 | } 22 | 23 | android { 24 | namespace = "io.getstream.chat.android.twitchclone.network" 25 | } 26 | 27 | dependencies { 28 | implementation(project(":core-model")) 29 | 30 | api(libs.okhttp.logging) 31 | api(libs.retrofit.core) 32 | api(libs.sandwich.retrofit) 33 | 34 | api(libs.retrofit.kotlin.serialization) 35 | api(libs.kotlinx.serialization.json) 36 | } 37 | -------------------------------------------------------------------------------- /core-network/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /core-network/src/main/kotlin/io/getstream/chat/android/twitchclone/network/TwitchDispatchers.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.network 18 | 19 | import javax.inject.Qualifier 20 | import kotlin.annotation.AnnotationRetention.RUNTIME 21 | 22 | @Qualifier 23 | @Retention(RUNTIME) 24 | annotation class Dispatcher(val twitchDispatchers: TwitchDispatchers) 25 | 26 | enum class TwitchDispatchers { 27 | IO 28 | } 29 | -------------------------------------------------------------------------------- /core-network/src/main/kotlin/io/getstream/chat/android/twitchclone/network/di/DispatchersModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.network.di 18 | 19 | import dagger.Module 20 | import dagger.Provides 21 | import dagger.hilt.InstallIn 22 | import dagger.hilt.components.SingletonComponent 23 | import io.getstream.chat.android.twitchclone.network.Dispatcher 24 | import io.getstream.chat.android.twitchclone.network.TwitchDispatchers 25 | import kotlinx.coroutines.CoroutineDispatcher 26 | import kotlinx.coroutines.Dispatchers 27 | 28 | @Module 29 | @InstallIn(SingletonComponent::class) 30 | internal object DispatchersModule { 31 | 32 | @Provides 33 | @Dispatcher(TwitchDispatchers.IO) 34 | fun providesIODispatcher(): CoroutineDispatcher = Dispatchers.IO 35 | } 36 | -------------------------------------------------------------------------------- /core-network/src/main/kotlin/io/getstream/chat/android/twitchclone/network/di/NetworkModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.network.di 18 | 19 | import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory 20 | import com.skydoves.sandwich.retrofit.adapters.ApiResponseCallAdapterFactory 21 | import dagger.Module 22 | import dagger.Provides 23 | import dagger.hilt.InstallIn 24 | import dagger.hilt.components.SingletonComponent 25 | import io.getstream.chat.android.twitchclone.network.service.RewardsService 26 | import javax.inject.Singleton 27 | import kotlinx.serialization.json.Json 28 | import okhttp3.MediaType.Companion.toMediaType 29 | import retrofit2.Retrofit 30 | import retrofit2.create 31 | 32 | @Module 33 | @InstallIn(SingletonComponent::class) 34 | internal object NetworkModule { 35 | 36 | @Provides 37 | @Singleton 38 | fun providesNetworkJson(): Json = Json { 39 | ignoreUnknownKeys = true 40 | } 41 | 42 | @Provides 43 | @Singleton 44 | fun provideTwitchRetrofit(networkJson: Json): Retrofit { 45 | return Retrofit.Builder() 46 | .baseUrl( 47 | "https://gist.githubusercontent.com/skydoves/c2da9e382222ba50aba7a01fcace76e1/" + 48 | "raw/d87a6fdfa4cff479d8dae7b32ac19a0c6d16817a/" 49 | ) 50 | .addConverterFactory(networkJson.asConverterFactory("application/json".toMediaType())) 51 | .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) 52 | .build() 53 | } 54 | 55 | @Provides 56 | @Singleton 57 | fun provideRewardsService( 58 | retrofit: Retrofit 59 | ): RewardsService { 60 | return retrofit.create() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core-network/src/main/kotlin/io/getstream/chat/android/twitchclone/network/service/RewardsService.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.network.service 18 | 19 | import com.skydoves.sandwich.ApiResponse 20 | import io.getstream.chat.android.twitchclone.model.Reward 21 | import retrofit2.http.GET 22 | 23 | interface RewardsService { 24 | 25 | @GET("TwitchCloneRewards.json") 26 | suspend fun fetchRewards(): ApiResponse> 27 | } 28 | -------------------------------------------------------------------------------- /feature-channels/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature-channels/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | @Suppress("DSL_SCOPE_VIOLATION") 17 | plugins { 18 | id("getstream.android.library") 19 | id("getstream.android.library.compose") 20 | id("getstream.android.feature") 21 | id("getstream.android.hilt") 22 | id("getstream.spotless") 23 | } 24 | 25 | android { 26 | namespace = "io.getstream.chat.android.twitchclone.channels" 27 | } 28 | 29 | dependencies { 30 | // Stream SDK 31 | implementation(libs.stream.chat.compose) 32 | 33 | // Compose 34 | implementation(libs.androidx.compose.ui) 35 | implementation(libs.androidx.compose.ui.tooling) 36 | implementation(libs.androidx.compose.foundation) 37 | implementation(libs.androidx.compose.material) 38 | implementation(libs.androidx.compose.runtime) 39 | } 40 | -------------------------------------------------------------------------------- /feature-channels/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /feature-channels/src/main/kotlin/io/getstream/chat/android/twitchclone/channels/StreamerInformation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.channels 18 | 19 | import androidx.compose.foundation.layout.Arrangement 20 | import androidx.compose.foundation.layout.Row 21 | import androidx.compose.foundation.layout.Spacer 22 | import androidx.compose.foundation.layout.size 23 | import androidx.compose.foundation.layout.width 24 | import androidx.compose.foundation.layout.wrapContentSize 25 | import androidx.compose.foundation.shape.CircleShape 26 | import androidx.compose.material.Text 27 | import androidx.compose.runtime.Composable 28 | import androidx.compose.ui.Alignment 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.draw.clip 31 | import androidx.compose.ui.layout.ContentScale 32 | import androidx.compose.ui.text.font.FontWeight 33 | import androidx.compose.ui.unit.dp 34 | import androidx.compose.ui.unit.sp 35 | import com.skydoves.landscapist.ImageOptions 36 | import com.skydoves.landscapist.animation.crossfade.CrossfadePlugin 37 | import com.skydoves.landscapist.coil.CoilImage 38 | import com.skydoves.landscapist.components.rememberImageComponent 39 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 40 | 41 | @Composable 42 | fun StreamerInformation( 43 | streamerAvatarImage: String?, 44 | streamerName: String?, 45 | modifier: Modifier = Modifier, 46 | verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, 47 | horizontalArrangement: Arrangement.Horizontal = Arrangement.Center 48 | ) { 49 | Row( 50 | modifier = modifier, 51 | verticalAlignment = verticalAlignment, 52 | horizontalArrangement = horizontalArrangement 53 | ) { 54 | CoilImage( 55 | modifier = Modifier 56 | .size(24.dp) 57 | .clip(CircleShape), 58 | imageModel = { streamerAvatarImage }, 59 | imageOptions = ImageOptions(contentScale = ContentScale.Crop), 60 | component = rememberImageComponent { 61 | +CrossfadePlugin() 62 | } 63 | ) 64 | 65 | Spacer(modifier = Modifier.width(6.dp)) 66 | 67 | if (streamerName != null) { 68 | Text( 69 | modifier = Modifier.wrapContentSize(), 70 | text = streamerName, 71 | style = ChatTheme.typography.bodyBold, 72 | fontSize = 14.sp, 73 | color = ChatTheme.colors.textHighEmphasis, 74 | fontWeight = FontWeight.Bold 75 | ) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /feature-livestream/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature-livestream/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | @Suppress("DSL_SCOPE_VIOLATION") 17 | plugins { 18 | id("getstream.android.library") 19 | id("getstream.android.library.compose") 20 | id("getstream.android.feature") 21 | id("getstream.android.hilt") 22 | id("getstream.spotless") 23 | } 24 | 25 | android { 26 | namespace = "io.getstream.chat.android.twitchclone.livestream" 27 | } 28 | 29 | dependencies { 30 | // feature module 31 | implementation(project(":feature-messages")) 32 | 33 | // Stream SDK 34 | implementation(libs.stream.chat.compose) 35 | implementation(libs.stream.video.compose) 36 | 37 | // accompanist 38 | implementation(libs.accompanist.permissions) 39 | 40 | // Compose 41 | implementation(libs.androidx.compose.ui) 42 | implementation(libs.androidx.compose.ui.tooling) 43 | implementation(libs.androidx.compose.foundation) 44 | implementation(libs.androidx.compose.material) 45 | implementation(libs.androidx.compose.runtime) 46 | } 47 | -------------------------------------------------------------------------------- /feature-livestream/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /feature-livestream/src/main/kotlin/io/getstream/chat/android/twitchclone/livestream/EnsurePermissions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.livestream 18 | 19 | import android.Manifest 20 | import android.os.Build 21 | import androidx.compose.runtime.Composable 22 | import androidx.compose.runtime.LaunchedEffect 23 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 24 | import com.google.accompanist.permissions.rememberMultiplePermissionsState 25 | 26 | @OptIn(ExperimentalPermissionsApi::class) 27 | @Composable 28 | fun EnsureVideoCallPermissions(onPermissionsGranted: () -> Unit) { 29 | // While the SDK will handle the microphone permission, 30 | // its not a bad idea to do it prior to entering any call UIs 31 | val permissionsState = rememberMultiplePermissionsState( 32 | permissions = buildList { 33 | // Access to camera & microphone 34 | add(Manifest.permission.CAMERA) 35 | add(Manifest.permission.RECORD_AUDIO) 36 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 37 | // Allow for foreground service for notification on API 26+ 38 | add(Manifest.permission.FOREGROUND_SERVICE) 39 | } 40 | } 41 | ) 42 | 43 | LaunchedEffect(key1 = Unit) { 44 | permissionsState.launchMultiplePermissionRequest() 45 | } 46 | 47 | LaunchedEffect(key1 = permissionsState.allPermissionsGranted) { 48 | if (permissionsState.allPermissionsGranted) { 49 | onPermissionsGranted() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /feature-livestream/src/main/kotlin/io/getstream/chat/android/twitchclone/livestream/LivestreamViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.livestream 18 | 19 | import androidx.lifecycle.ViewModel 20 | import io.getstream.video.android.core.Call 21 | import io.getstream.video.android.core.StreamVideo 22 | import kotlinx.coroutines.flow.MutableStateFlow 23 | import kotlinx.coroutines.flow.StateFlow 24 | 25 | class LivestreamViewModel : ViewModel() { 26 | 27 | private val livestreamUiStateMutableState = 28 | MutableStateFlow(LivestreamUiState.Loading) 29 | val livestreamUiState: StateFlow = livestreamUiStateMutableState 30 | 31 | suspend fun joinCall(type: String, id: String) { 32 | val streamVideo = StreamVideo.instance() 33 | val activeCall = streamVideo.state.activeCall.value 34 | val call = if (activeCall != null) { 35 | if (activeCall.id != id) { 36 | // If the call id is different leave the previous call 37 | activeCall.leave() 38 | // Return a new call 39 | streamVideo.call(type = type, id = id) 40 | } else { 41 | // Call ID is the same, use the active call 42 | activeCall 43 | } 44 | } else { 45 | // There is no active call, create new call 46 | streamVideo.call(type = type, id = id) 47 | } 48 | val result = call.join(create = true) 49 | result.onSuccess { 50 | livestreamUiStateMutableState.value = LivestreamUiState.Success(call) 51 | }.onError { 52 | livestreamUiStateMutableState.value = LivestreamUiState.Error 53 | } 54 | } 55 | 56 | override fun onCleared() { 57 | super.onCleared() 58 | } 59 | } 60 | 61 | sealed interface LivestreamUiState { 62 | 63 | data object Loading : LivestreamUiState 64 | 65 | data class Success(val call: Call) : LivestreamUiState 66 | 67 | data object Error : LivestreamUiState 68 | } 69 | -------------------------------------------------------------------------------- /feature-messages/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature-messages/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | @Suppress("DSL_SCOPE_VIOLATION") 17 | plugins { 18 | id("getstream.android.library") 19 | id("getstream.android.library.compose") 20 | id("getstream.android.feature") 21 | id("getstream.android.hilt") 22 | id("getstream.spotless") 23 | } 24 | 25 | android { 26 | namespace = "io.getstream.chat.android.twitchclone.messages" 27 | } 28 | 29 | dependencies { 30 | // Stream SDK 31 | implementation(libs.stream.chat.compose) 32 | 33 | // Compose 34 | implementation(libs.androidx.compose.ui) 35 | implementation(libs.androidx.compose.ui.tooling) 36 | implementation(libs.androidx.compose.foundation) 37 | implementation(libs.androidx.compose.material) 38 | implementation(libs.androidx.compose.runtime) 39 | } 40 | -------------------------------------------------------------------------------- /feature-messages/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/extensions/MessageExtensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.extensions 18 | 19 | import androidx.compose.foundation.layout.fillMaxSize 20 | import androidx.compose.foundation.layout.padding 21 | import androidx.compose.foundation.text.InlineTextContent 22 | import androidx.compose.foundation.text.appendInlineContent 23 | import androidx.compose.ui.Modifier 24 | import androidx.compose.ui.text.AnnotatedString 25 | import androidx.compose.ui.text.Placeholder 26 | import androidx.compose.ui.text.PlaceholderVerticalAlign 27 | import androidx.compose.ui.text.buildAnnotatedString 28 | import androidx.compose.ui.unit.dp 29 | import androidx.compose.ui.unit.sp 30 | import com.skydoves.landscapist.coil.CoilImage 31 | import io.getstream.chat.android.twitchclone.designsystem.assets.AssetUtils 32 | import io.getstream.chat.android.twitchclone.designsystem.assets.AssetsProvider 33 | 34 | fun String.transformText(): Pair, AnnotatedString> { 35 | val emotes = AssetsProvider.reactions 36 | val emoteKeys = emotes.keys 37 | val parts = this.split(" ") 38 | val addedEmotes = parts.filter { it in emotes } 39 | 40 | val inlineContent = addedEmotes.associateWith { badgeName -> 41 | InlineTextContent( 42 | Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter) 43 | ) { 44 | CoilImage( 45 | modifier = Modifier 46 | .fillMaxSize() 47 | .padding(2.dp), 48 | imageModel = { AssetUtils.getEmotePath(emotes[badgeName]) } 49 | ) 50 | } 51 | } 52 | 53 | val annotatedString = buildAnnotatedString { 54 | for (part in parts) { 55 | if (part in emoteKeys) { 56 | appendInlineContent(id = part) 57 | } else { 58 | append(part) 59 | } 60 | 61 | if (parts.indexOf(part) != parts.lastIndex) { 62 | append(" ") 63 | } 64 | } 65 | } 66 | 67 | return inlineContent to annotatedString 68 | } 69 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/extensions/UserExtensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.extensions 18 | 19 | import io.getstream.chat.android.models.User 20 | import io.getstream.chat.android.twitchclone.designsystem.assets.AssetsProvider 21 | 22 | val User.nameColor: String 23 | inline get() = when { 24 | "Seth" in name -> "#a9a1b7" 25 | "John" in name -> "#cbcd3d" 26 | "iOS" in name -> "#fe5c17" 27 | "Steven" in name -> "#76d927" 28 | "Marin" in name -> "#175ad0" 29 | "Juan" in name -> "#18c090" 30 | "Jaewoong" in name -> "#005FFF" 31 | else -> "#808080" 32 | } 33 | 34 | val User.badges: List 35 | inline get() { 36 | val badges = AssetsProvider.userBadges.values.toList() 37 | 38 | return when { 39 | "Seth" in name -> listOf(badges[0], badges[2], badges[3]) 40 | "John" in name -> listOf(badges[0], badges[1]) 41 | "iOS" in name -> listOf(badges[5]) 42 | "Steven" in name -> listOf(badges[1], badges[5]) 43 | "Marin" in name -> listOf(badges[2], badges[4]) 44 | "Juan" in name -> listOf(badges[1], badges[2], badges[3], badges[4]) 45 | "Jaewoong" in name -> listOf(badges[0], badges[1], badges[2], badges[3], badges[4], badges[5]) 46 | else -> listOf(badges[2], badges[5]) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/emotes/EmotesContent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.emotes 18 | 19 | import androidx.compose.foundation.clickable 20 | import androidx.compose.foundation.interaction.MutableInteractionSource 21 | import androidx.compose.foundation.layout.Box 22 | import androidx.compose.foundation.layout.fillMaxSize 23 | import androidx.compose.foundation.layout.padding 24 | import androidx.compose.foundation.layout.size 25 | import androidx.compose.foundation.lazy.grid.GridCells 26 | import androidx.compose.foundation.lazy.grid.LazyVerticalGrid 27 | import androidx.compose.foundation.lazy.grid.items 28 | import androidx.compose.material.ripple.rememberRipple 29 | import androidx.compose.runtime.Composable 30 | import androidx.compose.runtime.remember 31 | import androidx.compose.ui.Alignment 32 | import androidx.compose.ui.Modifier 33 | import androidx.compose.ui.unit.dp 34 | import com.skydoves.landscapist.coil.CoilImage 35 | import io.getstream.chat.android.twitchclone.designsystem.assets.AssetUtils 36 | 37 | @Composable 38 | fun EmotesContent( 39 | emotes: Map, 40 | cells: GridCells, 41 | modifier: Modifier = Modifier, 42 | onEmoteSelected: (String) -> Unit = {} 43 | ) { 44 | LazyVerticalGrid(modifier = modifier, columns = cells) { 45 | items(emotes.values.toList(), key = { it }) { emote -> 46 | Box(modifier = Modifier.fillMaxSize()) { 47 | CoilImage( 48 | modifier = Modifier 49 | .padding(4.dp) 50 | .size(24.dp) 51 | .align(Alignment.Center) 52 | .clickable( 53 | indication = rememberRipple(bounded = false), 54 | interactionSource = remember { MutableInteractionSource() } 55 | ) { 56 | val emoteName = emote.split(".")[0] 57 | onEmoteSelected(":$emoteName: ") 58 | }, 59 | imageModel = { AssetUtils.getEmotePath(emote) } 60 | ) 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/messages/ChatSettingsIcon.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.messages 18 | 19 | import androidx.compose.material.Icon 20 | import androidx.compose.material.IconButton 21 | import androidx.compose.runtime.Composable 22 | import androidx.compose.ui.res.painterResource 23 | import androidx.compose.ui.res.stringResource 24 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 25 | import io.getstream.chat.android.twitchclone.messages.R 26 | 27 | @Composable 28 | fun ChatSettingsIcon(onClick: () -> Unit = {}) { 29 | IconButton(onClick = onClick) { 30 | Icon( 31 | painter = painterResource(id = R.drawable.ic_vertical_dots), 32 | tint = ChatTheme.colors.textLowEmphasis, 33 | contentDescription = stringResource(id = R.string.accessibility_open_chat_settings) 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/messages/SendButton.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.messages 18 | 19 | import androidx.compose.material.Icon 20 | import androidx.compose.material.IconButton 21 | import androidx.compose.runtime.Composable 22 | import androidx.compose.ui.res.painterResource 23 | import androidx.compose.ui.res.stringResource 24 | import io.getstream.chat.android.compose.R 25 | import io.getstream.chat.android.compose.ui.components.composer.CoolDownIndicator 26 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 27 | import io.getstream.chat.android.models.Attachment 28 | import io.getstream.chat.android.ui.common.state.messages.composer.ValidationError 29 | 30 | @Composable 31 | fun SendButton( 32 | value: String, 33 | coolDownTime: Int, 34 | attachments: List, 35 | validationErrors: List, 36 | onSendMessage: (String, List) -> Unit 37 | ) { 38 | val isInputValid = 39 | (value.isNotEmpty() || attachments.isNotEmpty()) && validationErrors.isEmpty() 40 | 41 | if (coolDownTime > 0) { 42 | CoolDownIndicator(coolDownTime = coolDownTime) 43 | } else { 44 | IconButton( 45 | enabled = isInputValid, 46 | content = { 47 | Icon( 48 | painter = painterResource(id = R.drawable.stream_compose_ic_send), 49 | contentDescription = stringResource(id = R.string.stream_compose_send_message), 50 | tint = if (isInputValid) { 51 | ChatTheme.colors.primaryAccent 52 | } else { 53 | ChatTheme.colors.textLowEmphasis 54 | } 55 | ) 56 | }, 57 | onClick = { 58 | if (isInputValid) { 59 | onSendMessage(value, attachments) 60 | } 61 | } 62 | ) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/messages/TwitchMessagesViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.messages 18 | 19 | import androidx.lifecycle.ViewModel 20 | import androidx.lifecycle.viewModelScope 21 | import com.skydoves.sandwich.getOrThrow 22 | import com.skydoves.sandwich.isSuccess 23 | import dagger.hilt.android.lifecycle.HiltViewModel 24 | import io.getstream.chat.android.twitchclone.data.coroutines.WhileSubscribedOrRetained 25 | import io.getstream.chat.android.twitchclone.data.repository.RewardRepository 26 | import io.getstream.chat.android.twitchclone.model.Reward 27 | import javax.inject.Inject 28 | import kotlinx.coroutines.flow.StateFlow 29 | import kotlinx.coroutines.flow.flatMapLatest 30 | import kotlinx.coroutines.flow.flowOf 31 | import kotlinx.coroutines.flow.stateIn 32 | 33 | @HiltViewModel 34 | class TwitchMessagesViewModel @Inject constructor( 35 | private val rewardRepository: RewardRepository 36 | ) : ViewModel() { 37 | 38 | val rewards: StateFlow = 39 | rewardRepository.getRewardStream() 40 | .flatMapLatest { 41 | if (it.isSuccess) { 42 | flowOf( 43 | TwitchMessageUiState.Success(it.getOrThrow()) 44 | ) 45 | } else { 46 | flowOf(TwitchMessageUiState.Error) 47 | } 48 | } 49 | .stateIn( 50 | scope = viewModelScope, 51 | started = WhileSubscribedOrRetained, 52 | initialValue = TwitchMessageUiState.Loading 53 | ) 54 | } 55 | 56 | sealed interface TwitchMessageUiState { 57 | 58 | data object Loading : TwitchMessageUiState 59 | 60 | data class Success(val rewards: List) : TwitchMessageUiState 61 | 62 | data object Error : TwitchMessageUiState 63 | } 64 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/player/TwitchVideoPlayer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.player 18 | 19 | import androidx.compose.foundation.background 20 | import androidx.compose.foundation.border 21 | import androidx.compose.foundation.clickable 22 | import androidx.compose.foundation.layout.Box 23 | import androidx.compose.foundation.layout.padding 24 | import androidx.compose.foundation.shape.CircleShape 25 | import androidx.compose.foundation.shape.RoundedCornerShape 26 | import androidx.compose.material.Icon 27 | import androidx.compose.material.Surface 28 | import androidx.compose.runtime.Composable 29 | import androidx.compose.ui.Alignment 30 | import androidx.compose.ui.Modifier 31 | import androidx.compose.ui.draw.clip 32 | import androidx.compose.ui.graphics.Color 33 | import androidx.compose.ui.res.painterResource 34 | import androidx.compose.ui.unit.dp 35 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 36 | import io.getstream.chat.android.compose.viewmodel.messages.MessageListViewModel 37 | import io.getstream.chat.android.twitchclone.messages.R 38 | import io.getstream.chat.android.twitchclone.model.streamLink 39 | 40 | @Composable 41 | fun TwitchVideoPlayer( 42 | modifier: Modifier, 43 | listViewModel: MessageListViewModel, 44 | onChannelInfoClick: () -> Unit 45 | ) { 46 | val videoShape = RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp) 47 | Box( 48 | modifier = modifier 49 | .clip(shape = videoShape) 50 | .background(ChatTheme.colors.appBackground) 51 | .border( 52 | width = 4.dp, 53 | color = ChatTheme.colors.primaryAccent.copy(0.75f), 54 | shape = videoShape 55 | ) 56 | ) { 57 | val streamLink = listViewModel.channel.streamLink 58 | 59 | if (streamLink != null) { 60 | VideoPlayer(videoUrl = streamLink) 61 | } 62 | 63 | Surface( 64 | modifier = Modifier 65 | .padding(12.dp) 66 | .align(Alignment.TopEnd) 67 | .clickable(onClick = onChannelInfoClick), 68 | color = Color.White, 69 | shape = CircleShape 70 | ) { 71 | Icon( 72 | painter = painterResource(id = R.drawable.ic_info), 73 | contentDescription = null, 74 | tint = ChatTheme.colors.primaryAccent 75 | ) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/player/VideoPlayer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.player 18 | 19 | import android.net.Uri 20 | import android.view.Gravity 21 | import android.view.ViewGroup 22 | import android.widget.FrameLayout 23 | import android.widget.VideoView 24 | import androidx.compose.foundation.background 25 | import androidx.compose.foundation.layout.BoxScope 26 | import androidx.compose.foundation.layout.fillMaxSize 27 | import androidx.compose.material.CircularProgressIndicator 28 | import androidx.compose.runtime.Composable 29 | import androidx.compose.runtime.LaunchedEffect 30 | import androidx.compose.runtime.getValue 31 | import androidx.compose.runtime.mutableStateOf 32 | import androidx.compose.runtime.remember 33 | import androidx.compose.runtime.setValue 34 | import androidx.compose.ui.Alignment 35 | import androidx.compose.ui.Modifier 36 | import androidx.compose.ui.platform.LocalContext 37 | import androidx.compose.ui.viewinterop.AndroidView 38 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 39 | 40 | @Composable 41 | fun BoxScope.VideoPlayer(videoUrl: String?) { 42 | val context = LocalContext.current 43 | var prepared by remember { mutableStateOf(false) } 44 | val videoView = remember { 45 | VideoView(context).apply { 46 | setOnPreparedListener { prepared = true } 47 | layoutParams = FrameLayout.LayoutParams( 48 | ViewGroup.LayoutParams.MATCH_PARENT, 49 | ViewGroup.LayoutParams.MATCH_PARENT 50 | ).apply { 51 | gravity = Gravity.CENTER 52 | } 53 | } 54 | } 55 | 56 | LaunchedEffect(Unit) { 57 | videoView.setVideoURI(Uri.parse(videoUrl)) 58 | videoView.start() 59 | } 60 | 61 | AndroidView( 62 | modifier = Modifier 63 | .fillMaxSize() 64 | .background(ChatTheme.colors.appBackground), 65 | factory = { videoView } 66 | ) 67 | 68 | if (!prepared) { 69 | CircularProgressIndicator( 70 | modifier = Modifier.align(Alignment.Center), 71 | color = ChatTheme.colors.primaryAccent 72 | ) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/rewards/RewardExtension.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.rewards 18 | 19 | import androidx.compose.material.icons.Icons 20 | import androidx.compose.material.icons.filled.AirplanemodeActive 21 | import androidx.compose.material.icons.filled.LockOpen 22 | import androidx.compose.material.icons.filled.Message 23 | import androidx.compose.material.icons.filled.RocketLaunch 24 | import androidx.compose.material.icons.filled.SafetyCheck 25 | import androidx.compose.material.icons.filled.StarBorder 26 | import androidx.compose.material.icons.filled.TagFaces 27 | import androidx.compose.ui.graphics.Color 28 | import androidx.compose.ui.graphics.vector.ImageVector 29 | import io.getstream.chat.android.twitchclone.model.Reward 30 | 31 | val Reward.background: Color 32 | inline get() = Color(android.graphics.Color.parseColor(color)) 33 | 34 | val Reward.iconVector: ImageVector 35 | inline get() = when (icon) { 36 | "LockOpen" -> Icons.Default.LockOpen 37 | "TagFaces" -> Icons.Default.TagFaces 38 | "Message" -> Icons.Default.Message 39 | "StarBorder" -> Icons.Default.StarBorder 40 | "AirplanemodeActive" -> Icons.Default.AirplanemodeActive 41 | "RocketLaunch" -> Icons.Default.RocketLaunch 42 | else -> Icons.Default.SafetyCheck 43 | } 44 | -------------------------------------------------------------------------------- /feature-messages/src/main/kotlin/io/getstream/chat/android/twitchclone/messages/ui/rewards/RewardIntegration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 io.getstream.chat.android.twitchclone.messages.ui.rewards 18 | 19 | import androidx.compose.foundation.layout.IntrinsicSize 20 | import androidx.compose.foundation.layout.Row 21 | import androidx.compose.foundation.layout.height 22 | import androidx.compose.foundation.layout.padding 23 | import androidx.compose.foundation.layout.size 24 | import androidx.compose.foundation.layout.wrapContentWidth 25 | import androidx.compose.material.IconButton 26 | import androidx.compose.material.Text 27 | import androidx.compose.runtime.Composable 28 | import androidx.compose.ui.Alignment 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.unit.TextUnit 31 | import androidx.compose.ui.unit.dp 32 | import com.skydoves.landscapist.coil.CoilImage 33 | import io.getstream.chat.android.compose.ui.theme.ChatTheme 34 | import io.getstream.chat.android.models.Channel 35 | import io.getstream.chat.android.twitchclone.model.pointsIcon 36 | 37 | @Composable 38 | fun RewardsIntegration( 39 | channel: Channel, 40 | rewardCount: Int, 41 | modifier: Modifier = Modifier, 42 | onClick: () -> Unit = {} 43 | ) { 44 | IconButton( 45 | onClick = onClick 46 | ) { 47 | Row(modifier = modifier) { 48 | CoilImage( 49 | modifier = Modifier 50 | .padding(vertical = 2.dp, horizontal = 4.dp) 51 | .size(16.dp), 52 | imageModel = { channel.pointsIcon } 53 | ) 54 | 55 | Text( 56 | modifier = Modifier 57 | .wrapContentWidth() 58 | .height(IntrinsicSize.Max) 59 | .align(Alignment.CenterVertically), 60 | text = rewardCount.toString(), 61 | fontSize = TextUnit.Unspecified, 62 | color = ChatTheme.colors.textLowEmphasis, 63 | style = ChatTheme.typography.bodyBold 64 | ) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /figures/figure0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/figure0.png -------------------------------------------------------------------------------- /figures/figure1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/figure1.png -------------------------------------------------------------------------------- /figures/figure2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/figure2.png -------------------------------------------------------------------------------- /figures/figure3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/figure3.png -------------------------------------------------------------------------------- /figures/figure4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/figure4.png -------------------------------------------------------------------------------- /figures/stream0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/stream0.png -------------------------------------------------------------------------------- /figures/stream1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/stream1.png -------------------------------------------------------------------------------- /figures/stream2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/stream2.png -------------------------------------------------------------------------------- /figures/stream3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/stream3.png -------------------------------------------------------------------------------- /figures/stream4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/stream4.png -------------------------------------------------------------------------------- /figures/stream5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/stream5.png -------------------------------------------------------------------------------- /figures/stream6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/figures/stream6.png -------------------------------------------------------------------------------- /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 | 10 | # https://docs.gradle.org/current/userguide/build_environment.html#sec:configuring_jvm_memory 11 | org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:MaxMetaspaceSize=512m -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g -Dlint.nullness.ignore-deprecated=true 12 | 13 | # https://docs.gradle.org/current/usqwerguide/build_cache.html 14 | org.gradle.caching=true 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | org.gradle.parallel=true 20 | 21 | # Configure only necessary projects, useful with multimodule projects 22 | org.gradle.configureondemand=true 23 | 24 | # AndroidX Migration https://developer.android.com/jetpack/androidx/migrate 25 | android.useAndroidX=true 26 | 27 | # Disable namespacing of each library's R class so that its R class includes only the 28 | # resources declared in the library itself and none from the library's dependencies, 29 | # thereby reducing the size of the R class for that library 30 | android.nonTransitiveRClass=false 31 | android.defaults.buildfeatures.buildconfig=true 32 | 33 | # Disabled R8 full mode 34 | android.enableR8.fullMode=false 35 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 13 09:13:24 KST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 5 | networkTimeout=10000 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /previews/preview0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/previews/preview0.png -------------------------------------------------------------------------------- /previews/preview1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/previews/preview1.png -------------------------------------------------------------------------------- /previews/preview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/previews/preview2.png -------------------------------------------------------------------------------- /previews/preview3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/previews/preview3.png -------------------------------------------------------------------------------- /previews/preview4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/previews/preview4.png -------------------------------------------------------------------------------- /previews/preview5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/twitch-clone-compose/665963a27c1282f3226ae0994c9906d4623a30c5/previews/preview5.png -------------------------------------------------------------------------------- /secrets.defaults.properties: -------------------------------------------------------------------------------- 1 | STREAM_API_KEY=aaaaaaaaaaaa 2 | -------------------------------------------------------------------------------- /secrets.properties: -------------------------------------------------------------------------------- 1 | STREAM_API_KEY=cpv4bsuedrft 2 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnstableApiUsage") 2 | pluginManagement { 3 | includeBuild("build-logic") 4 | repositories { 5 | gradlePluginPortal() 6 | google() 7 | mavenCentral() 8 | maven(url = "https://plugins.gradle.org/m2/") 9 | } 10 | } 11 | dependencyResolutionManagement { 12 | repositories { 13 | google() 14 | mavenCentral() 15 | maven(url = "https://plugins.gradle.org/m2/") 16 | } 17 | } 18 | 19 | rootProject.name = "TwitchCloneCompose" 20 | include(":app") 21 | include(":core-designsystem") 22 | include(":core-navigation") 23 | include(":core-model") 24 | include(":core-network") 25 | include(":core-database") 26 | include(":core-data") 27 | include(":feature-channels") 28 | include(":feature-messages") 29 | include(":feature-livestream") 30 | include(":baselineprofile") 31 | -------------------------------------------------------------------------------- /spotless/copyright.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://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 | -------------------------------------------------------------------------------- /spotless/copyright.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Stream.IO, Inc. All Rights Reserved. 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 | * http://www.apache.org/licenses/LICENSE-2. 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 | -------------------------------------------------------------------------------- /spotless/copyright.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | -------------------------------------------------------------------------------- /spotless/spotless.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.diffplug.spotless" 2 | 3 | spotless { 4 | kotlin { 5 | target "**/*.kt" 6 | targetExclude "**/build/**/*.kt" 7 | ktlint().setUseExperimental(true).editorConfigOverride(['indent_size': '2', 'continuation_indent_size': '2']) 8 | licenseHeaderFile "$rootDir/spotless/copyright.kt" 9 | trimTrailingWhitespace() 10 | endWithNewline() 11 | } 12 | } 13 | --------------------------------------------------------------------------------