Skip to content

Commit 0fcf6c4

Browse files
authored
Merge pull request #140 from daniebeler/dev
Version 3.3.0
2 parents ed8b1b0 + a8f5839 commit 0fcf6c4

File tree

128 files changed

+8990
-790
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+8990
-790
lines changed

.github/workflows/crowdin.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Crowdin Action
2+
3+
on:
4+
push:
5+
branches: [ dev ]
6+
workflow_dispatch:
7+
8+
jobs:
9+
synchronize-with-crowdin:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: crowdin action
17+
uses: crowdin/github-action@v2
18+
with:
19+
upload_sources: true
20+
upload_translations: false
21+
download_translations: true
22+
localization_branch_name: l10n_crowdin_translations
23+
create_pull_request: true
24+
pull_request_title: 'New Crowdin Translations'
25+
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
26+
pull_request_base_branch_name: 'dev'
27+
pull_request_assignees: 'crowdin-bot'
28+
env:
29+
# A classic GitHub Personal Access Token with the 'repo' scope selected (the user should have write access to the repository).
30+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31+
32+
# A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api
33+
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
34+
35+
# Visit https://crowdin.com/settings#api-key to create this token
36+
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ android {
1717
applicationId = "com.daniebeler.pfpixelix"
1818
minSdk = 26
1919
targetSdk = 35
20-
versionCode = 23
21-
versionName = "3.2.1"
20+
versionCode = 24
21+
versionName = "3.3.0"
2222

2323
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2424
vectorDrawables {

app/src/main/java/com/daniebeler/pfpixelix/MainActivity.kt

Lines changed: 123 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,43 @@ import androidx.activity.compose.setContent
99
import androidx.activity.enableEdgeToEdge
1010
import androidx.compose.animation.EnterTransition
1111
import androidx.compose.animation.ExitTransition
12+
import androidx.compose.foundation.interaction.MutableInteractionSource
13+
import androidx.compose.foundation.interaction.PressInteraction
1214
import androidx.compose.foundation.layout.Box
15+
import androidx.compose.foundation.layout.Row
16+
import androidx.compose.foundation.layout.WindowInsets
17+
import androidx.compose.foundation.layout.asPaddingValues
18+
import androidx.compose.foundation.layout.height
19+
import androidx.compose.foundation.layout.navigationBars
1320
import androidx.compose.foundation.layout.padding
1421
import androidx.compose.foundation.layout.size
22+
import androidx.compose.foundation.layout.width
23+
import androidx.compose.foundation.shape.CircleShape
24+
import androidx.compose.material.icons.Icons
25+
import androidx.compose.material.icons.outlined.UnfoldMore
26+
import androidx.compose.material3.ExperimentalMaterial3Api
1527
import androidx.compose.material3.Icon
1628
import androidx.compose.material3.MaterialTheme
29+
import androidx.compose.material3.ModalBottomSheet
1730
import androidx.compose.material3.NavigationBar
1831
import androidx.compose.material3.NavigationBarItem
1932
import androidx.compose.material3.NavigationBarItemDefaults
2033
import androidx.compose.material3.Scaffold
34+
import androidx.compose.material3.rememberModalBottomSheetState
2135
import androidx.compose.runtime.Composable
2236
import androidx.compose.runtime.LaunchedEffect
2337
import androidx.compose.runtime.getValue
38+
import androidx.compose.runtime.mutableStateOf
39+
import androidx.compose.runtime.remember
40+
import androidx.compose.runtime.rememberCoroutineScope
41+
import androidx.compose.runtime.setValue
42+
import androidx.compose.ui.Alignment
2443
import androidx.compose.ui.Modifier
44+
import androidx.compose.ui.draw.clip
2545
import androidx.compose.ui.graphics.Color
46+
import androidx.compose.ui.res.painterResource
47+
import androidx.compose.ui.res.stringResource
48+
import androidx.compose.ui.unit.Density
2649
import androidx.compose.ui.unit.dp
2750
import androidx.core.view.WindowCompat
2851
import androidx.navigation.NavHostController
@@ -31,6 +54,7 @@ import androidx.navigation.compose.composable
3154
import androidx.navigation.compose.currentBackStackEntryAsState
3255
import androidx.navigation.compose.rememberNavController
3356
import androidx.navigation.navArgument
57+
import coil.compose.AsyncImage
3458
import com.daniebeler.pfpixelix.common.Destinations
3559
import com.daniebeler.pfpixelix.di.HostSelectionInterceptorInterface
3660
import com.daniebeler.pfpixelix.domain.model.LoginData
@@ -43,12 +67,14 @@ import com.daniebeler.pfpixelix.ui.composables.direct_messages.chat.ChatComposab
4367
import com.daniebeler.pfpixelix.ui.composables.direct_messages.conversations.ConversationsComposable
4468
import com.daniebeler.pfpixelix.ui.composables.edit_post.EditPostComposable
4569
import com.daniebeler.pfpixelix.ui.composables.edit_profile.EditProfileComposable
70+
import com.daniebeler.pfpixelix.ui.composables.explore.ExploreComposable
4671
import com.daniebeler.pfpixelix.ui.composables.followers.FollowersMainComposable
72+
import com.daniebeler.pfpixelix.ui.composables.mention.MentionComposable
4773
import com.daniebeler.pfpixelix.ui.composables.newpost.NewPostComposable
4874
import com.daniebeler.pfpixelix.ui.composables.notifications.NotificationsComposable
4975
import com.daniebeler.pfpixelix.ui.composables.profile.other_profile.OtherProfileComposable
76+
import com.daniebeler.pfpixelix.ui.composables.profile.own_profile.AccountSwitchBottomSheet
5077
import com.daniebeler.pfpixelix.ui.composables.profile.own_profile.OwnProfileComposable
51-
import com.daniebeler.pfpixelix.ui.composables.search.SearchComposable
5278
import com.daniebeler.pfpixelix.ui.composables.settings.about_instance.AboutInstanceComposable
5379
import com.daniebeler.pfpixelix.ui.composables.settings.about_pixelix.AboutPixelixComposable
5480
import com.daniebeler.pfpixelix.ui.composables.settings.blocked_accounts.BlockedAccountsComposable
@@ -60,11 +86,13 @@ import com.daniebeler.pfpixelix.ui.composables.settings.muted_accounts.MutedAcco
6086
import com.daniebeler.pfpixelix.ui.composables.settings.preferences.PreferencesComposable
6187
import com.daniebeler.pfpixelix.ui.composables.single_post.SinglePostComposable
6288
import com.daniebeler.pfpixelix.ui.composables.timelines.hashtag_timeline.HashtagTimelineComposable
63-
import com.daniebeler.pfpixelix.ui.composables.trending.TrendingComposable
6489
import com.daniebeler.pfpixelix.ui.theme.PixelixTheme
6590
import com.daniebeler.pfpixelix.utils.Navigate
6691
import dagger.hilt.android.AndroidEntryPoint
92+
import kotlinx.coroutines.cancelChildren
93+
import kotlinx.coroutines.delay
6794
import kotlinx.coroutines.flow.firstOrNull
95+
import kotlinx.coroutines.launch
6896
import kotlinx.coroutines.runBlocking
6997
import javax.inject.Inject
7098

@@ -91,12 +119,13 @@ class MainActivity : ComponentActivity() {
91119
}
92120
}
93121

122+
@OptIn(ExperimentalMaterial3Api::class)
94123
override fun onCreate(savedInstanceState: Bundle?) {
95124

96125
super.onCreate(savedInstanceState)
97126
enableEdgeToEdge()
98127
WindowCompat.setDecorFitsSystemWindows(window, false)
99-
128+
var avatar = ""
100129
runBlocking {
101130
val loginData: LoginData? = currentLoginDataUseCase()
102131
if (loginData == null || loginData.accessToken.isBlank() || loginData.baseUrl.isBlank()) {
@@ -119,23 +148,30 @@ class MainActivity : ComponentActivity() {
119148
)
120149
)
121150
}
151+
avatar = loginData.avatar
122152
}
123153
}
124154

125155
setContent {
156+
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
157+
var showAccountSwitchBottomSheet by remember { mutableStateOf(false) }
158+
126159
PixelixTheme {
127160
val navController: NavHostController = rememberNavController()
128161

129-
Scaffold(bottomBar = {
130-
BottomBar(navController = navController)
162+
Scaffold(contentWindowInsets = WindowInsets(0.dp), bottomBar = {
163+
BottomBar(
164+
navController = navController,
165+
avatar = avatar,
166+
openAccountSwitchBottomSheet = { showAccountSwitchBottomSheet = true },
167+
context = this
168+
)
131169
}) { paddingValues ->
132170
Box(
133171
modifier = Modifier.padding(paddingValues)
134172
) {
135-
136-
137173
NavigationGraph(
138-
navController = navController
174+
navController = navController,
139175
)
140176
val destination = intent.extras?.getString(KEY_DESTINATION) ?: ""
141177
if (destination.isNotBlank()) {
@@ -175,11 +211,23 @@ class MainActivity : ComponentActivity() {
175211

176212

177213
}
214+
if (showAccountSwitchBottomSheet) {
215+
ModalBottomSheet(
216+
onDismissRequest = {
217+
showAccountSwitchBottomSheet = false
218+
}, sheetState = sheetState
219+
) {
220+
AccountSwitchBottomSheet(closeBottomSheet = {
221+
showAccountSwitchBottomSheet = false
222+
}, null)
223+
}
224+
}
178225
}
179226
}
180227
}
181228
}
182229

230+
183231
fun updateAuthToV2(context: Context, baseUrl: String, accessToken: String) {
184232
val intent = Intent(context, LoginActivity::class.java)
185233
intent.putExtra("base_url", baseUrl)
@@ -207,10 +255,6 @@ fun NavigationGraph(navController: NavHostController) {
207255
composable(Destinations.HomeScreen.route) {
208256
HomeComposable(navController)
209257
}
210-
composable(Destinations.TrendingScreen.route) { navBackStackEntry ->
211-
val page = navBackStackEntry.arguments?.getString("page") ?: "posts"
212-
TrendingComposable(navController, page)
213-
}
214258

215259
composable(Destinations.NotificationsScreen.route) {
216260
NotificationsComposable(navController)
@@ -329,7 +373,7 @@ fun NavigationGraph(navController: NavHostController) {
329373
}
330374

331375
composable(Destinations.Search.route) {
332-
SearchComposable(navController)
376+
ExploreComposable(navController)
333377
}
334378

335379
composable(Destinations.Conversation.route) {
@@ -342,37 +386,90 @@ fun NavigationGraph(navController: NavHostController) {
342386
ChatComposable(navController = navController, accountId = id)
343387
}
344388
}
389+
390+
composable(Destinations.Mention.route) { navBackStackEntry ->
391+
val mentionId = navBackStackEntry.arguments?.getString("mentionid")
392+
mentionId?.let { id ->
393+
MentionComposable(navController = navController, mentionId = id)
394+
}
395+
}
345396
}
346397
}
347398

348399
@Composable
349-
fun BottomBar(navController: NavHostController) {
400+
fun BottomBar(
401+
navController: NavHostController,
402+
avatar: String,
403+
openAccountSwitchBottomSheet: () -> Unit,
404+
context: Context
405+
) {
350406
val screens = listOf(
351407
Destinations.HomeScreen,
352408
Destinations.Search,
353-
Destinations.TrendingScreen,
354409
Destinations.NotificationsScreen,
355410
Destinations.OwnProfile
356411
)
412+
val systemNavigationBarHeight =
413+
WindowInsets.navigationBars.asPaddingValues(Density(context)).calculateBottomPadding()
357414

358-
NavigationBar {
415+
NavigationBar(
416+
modifier = Modifier.height(60.dp + systemNavigationBarHeight)
417+
) {
359418
val navBackStackEntry by navController.currentBackStackEntryAsState()
360419
val currentRoute = navBackStackEntry?.destination?.route
361-
362420
screens.forEach { screen ->
421+
val interactionSource = remember { MutableInteractionSource() }
422+
val coroutineScope = rememberCoroutineScope()
423+
var isLongPress by remember { mutableStateOf(false) }
424+
425+
LaunchedEffect(interactionSource) {
426+
interactionSource.interactions.collect { interaction ->
427+
when (interaction) {
428+
is PressInteraction.Press -> {
429+
isLongPress = false // Reset flag before starting detection
430+
coroutineScope.launch {
431+
delay(500L) // Long-press threshold
432+
if (screen.route == Destinations.OwnProfile.route) {
433+
openAccountSwitchBottomSheet()
434+
}
435+
isLongPress = true
436+
}
437+
}
363438

439+
is PressInteraction.Release, is PressInteraction.Cancel -> {
440+
coroutineScope.coroutineContext.cancelChildren()
441+
}
442+
}
443+
}
444+
}
364445
NavigationBarItem(icon = {
365-
if (currentRoute == screen.route) {
446+
if (screen.route == Destinations.OwnProfile.route && avatar.isNotBlank()) {
447+
Row(verticalAlignment = Alignment.CenterVertically) {
448+
AsyncImage(
449+
model = avatar,
450+
error = painterResource(id = R.drawable.default_avatar),
451+
contentDescription = "",
452+
modifier = Modifier
453+
.height(30.dp)
454+
.width(30.dp)
455+
.clip(CircleShape)
456+
)
457+
Icon(
458+
Icons.Outlined.UnfoldMore,
459+
contentDescription = "long press to switch account"
460+
)
461+
}
462+
} else if (currentRoute == screen.route) {
366463
Icon(
367464
imageVector = screen.activeIcon!!,
368-
modifier = Modifier.size(32.dp),
369-
contentDescription = ""
465+
modifier = Modifier.size(30.dp),
466+
contentDescription = stringResource(screen.label)
370467
)
371468
} else {
372469
Icon(
373470
imageVector = screen.icon!!,
374-
modifier = Modifier.size(32.dp),
375-
contentDescription = ""
471+
modifier = Modifier.size(30.dp),
472+
contentDescription = stringResource(screen.label)
376473
)
377474
}
378475
},
@@ -381,8 +478,11 @@ fun BottomBar(navController: NavHostController) {
381478
selectedIconColor = MaterialTheme.colorScheme.inverseSurface,
382479
indicatorColor = Color.Transparent
383480
),
481+
interactionSource = interactionSource,
384482
onClick = {
385-
Navigate.navigateWithPopUp(screen.route, navController)
483+
if (!isLongPress) {
484+
Navigate.navigateWithPopUp(screen.route, navController)
485+
}
386486
})
387487
}
388488
}

0 commit comments

Comments
 (0)