mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 16:33:27 +00:00
Merge remote-tracking branch 'origin/main' into claude/cashu-wallet-wizard-0zr280
This commit is contained in:
@@ -348,6 +348,16 @@ composeCompiler {
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
|
||||
// Compose composition tracing — DEBUG ONLY, profiling aid (not shipped). Makes each
|
||||
// recomposition show up as a NAMED slice in Perfetto system traces so we can see which
|
||||
// composable recomposes (e.g. during the cold-start feed first-paint). All Apache-2.0.
|
||||
// Usage: runtime-enable, then capture a Perfetto trace with the `track_event` data source:
|
||||
// adb shell am broadcast -a androidx.tracing.perfetto.action.ENABLE_TRACING \
|
||||
// -n com.vitorpamplona.amethyst.debug/androidx.tracing.perfetto.TracingReceiver
|
||||
debugImplementation("androidx.compose.runtime:runtime-tracing")
|
||||
debugImplementation("androidx.tracing:tracing-perfetto:1.0.0")
|
||||
debugImplementation("androidx.tracing:tracing-perfetto-binary:1.0.0")
|
||||
|
||||
implementation(project(":quartz"))
|
||||
implementation(project(":commons"))
|
||||
implementation(project(":nestsClient"))
|
||||
|
||||
@@ -108,6 +108,7 @@ private object PrefKeys {
|
||||
const val DEFAULT_NAPPLETS_FOLLOW_LIST = "defaultNappletsFollowList"
|
||||
const val DEFAULT_NSITES_FOLLOW_LIST = "defaultNsitesFollowList"
|
||||
const val DEFAULT_WORKOUTS_FOLLOW_LIST = "defaultWorkoutsFollowList"
|
||||
const val DEFAULT_GIT_REPOSITORIES_FOLLOW_LIST = "defaultGitRepositoriesFollowList"
|
||||
const val DEFAULT_CALENDARS_FOLLOW_LIST = "defaultCalendarsFollowList"
|
||||
const val DEFAULT_PRODUCTS_FOLLOW_LIST = "defaultProductsFollowList"
|
||||
const val DEFAULT_SHORTS_FOLLOW_LIST = "defaultShortsFollowList"
|
||||
@@ -426,6 +427,7 @@ object LocalPreferences {
|
||||
putString(PrefKeys.DEFAULT_NAPPLETS_FOLLOW_LIST, JsonMapper.toJson(settings.defaultNappletsFollowList.value))
|
||||
putString(PrefKeys.DEFAULT_NSITES_FOLLOW_LIST, JsonMapper.toJson(settings.defaultNsitesFollowList.value))
|
||||
putString(PrefKeys.DEFAULT_WORKOUTS_FOLLOW_LIST, JsonMapper.toJson(settings.defaultWorkoutsFollowList.value))
|
||||
putString(PrefKeys.DEFAULT_GIT_REPOSITORIES_FOLLOW_LIST, JsonMapper.toJson(settings.defaultGitRepositoriesFollowList.value))
|
||||
putString(PrefKeys.DEFAULT_CALENDARS_FOLLOW_LIST, JsonMapper.toJson(settings.defaultCalendarsFollowList.value))
|
||||
putString(PrefKeys.DEFAULT_PRODUCTS_FOLLOW_LIST, JsonMapper.toJson(settings.defaultProductsFollowList.value))
|
||||
putString(PrefKeys.DEFAULT_SHORTS_FOLLOW_LIST, JsonMapper.toJson(settings.defaultShortsFollowList.value))
|
||||
@@ -790,6 +792,7 @@ object LocalPreferences {
|
||||
defaultNappletsFollowList = MutableStateFlow(followListPrefs.napplets),
|
||||
defaultNsitesFollowList = MutableStateFlow(followListPrefs.nsites),
|
||||
defaultWorkoutsFollowList = MutableStateFlow(followListPrefs.workouts),
|
||||
defaultGitRepositoriesFollowList = MutableStateFlow(followListPrefs.gitRepositories),
|
||||
defaultCalendarsFollowList = MutableStateFlow(followListPrefs.calendars),
|
||||
defaultProductsFollowList = MutableStateFlow(followListPrefs.products),
|
||||
defaultShortsFollowList = MutableStateFlow(followListPrefs.shorts),
|
||||
@@ -882,6 +885,7 @@ object LocalPreferences {
|
||||
val napplets: TopFilter,
|
||||
val nsites: TopFilter,
|
||||
val workouts: TopFilter,
|
||||
val gitRepositories: TopFilter,
|
||||
val calendars: TopFilter,
|
||||
val products: TopFilter,
|
||||
val shorts: TopFilter,
|
||||
@@ -937,6 +941,7 @@ object LocalPreferences {
|
||||
napplets = parseTopFilterOrDefault(getString(PrefKeys.DEFAULT_NAPPLETS_FOLLOW_LIST, null), TopFilter.Global),
|
||||
nsites = parseTopFilterOrDefault(getString(PrefKeys.DEFAULT_NSITES_FOLLOW_LIST, null), TopFilter.Global),
|
||||
workouts = parseTopFilterOrDefault(getString(PrefKeys.DEFAULT_WORKOUTS_FOLLOW_LIST, null), TopFilter.Global),
|
||||
gitRepositories = parseTopFilterOrDefault(getString(PrefKeys.DEFAULT_GIT_REPOSITORIES_FOLLOW_LIST, null), TopFilter.Global),
|
||||
calendars = parseTopFilterOrDefault(getString(PrefKeys.DEFAULT_CALENDARS_FOLLOW_LIST, null), TopFilter.Global),
|
||||
products = parseTopFilterOrDefault(getString(PrefKeys.DEFAULT_PRODUCTS_FOLLOW_LIST, null), TopFilter.AroundMe),
|
||||
shorts = parseTopFilterOrDefault(getString(PrefKeys.DEFAULT_SHORTS_FOLLOW_LIST, null), TopFilter.Global),
|
||||
|
||||
@@ -543,6 +543,9 @@ class Account(
|
||||
val liveWorkoutsFollowLists: StateFlow<IFeedTopNavFilter> = topNavFilterFlow(settings.defaultWorkoutsFollowList)
|
||||
val liveWorkoutsFollowListsPerRelay = OutboxLoaderState(liveWorkoutsFollowLists, cache, scope).flow
|
||||
|
||||
val liveGitRepositoriesFollowLists: StateFlow<IFeedTopNavFilter> = topNavFilterFlow(settings.defaultGitRepositoriesFollowList)
|
||||
val liveGitRepositoriesFollowListsPerRelay = OutboxLoaderState(liveGitRepositoriesFollowLists, cache, scope).flow
|
||||
|
||||
val liveCalendarsFollowLists: StateFlow<IFeedTopNavFilter> = topNavFilterFlow(settings.defaultCalendarsFollowList)
|
||||
val liveCalendarsFollowListsPerRelay = OutboxLoaderState(liveCalendarsFollowLists, cache, scope).flow
|
||||
|
||||
|
||||
@@ -192,6 +192,7 @@ class AccountSettings(
|
||||
val defaultNappletsFollowList: MutableStateFlow<TopFilter> = MutableStateFlow(TopFilter.Global),
|
||||
val defaultNsitesFollowList: MutableStateFlow<TopFilter> = MutableStateFlow(TopFilter.Global),
|
||||
val defaultWorkoutsFollowList: MutableStateFlow<TopFilter> = MutableStateFlow(TopFilter.Global),
|
||||
val defaultGitRepositoriesFollowList: MutableStateFlow<TopFilter> = MutableStateFlow(TopFilter.Global),
|
||||
val defaultCalendarsFollowList: MutableStateFlow<TopFilter> = MutableStateFlow(TopFilter.Global),
|
||||
val defaultProductsFollowList: MutableStateFlow<TopFilter> = MutableStateFlow(TopFilter.AroundMe),
|
||||
val defaultShortsFollowList: MutableStateFlow<TopFilter> = MutableStateFlow(TopFilter.Global),
|
||||
@@ -675,6 +676,17 @@ class AccountSettings(
|
||||
}
|
||||
}
|
||||
|
||||
fun changeDefaultGitRepositoriesFollowList(name: FeedDefinition) {
|
||||
changeDefaultGitRepositoriesFollowList(name.code)
|
||||
}
|
||||
|
||||
fun changeDefaultGitRepositoriesFollowList(name: TopFilter) {
|
||||
if (defaultGitRepositoriesFollowList.value != name) {
|
||||
defaultGitRepositoriesFollowList.tryEmit(name)
|
||||
saveAccountSettings()
|
||||
}
|
||||
}
|
||||
|
||||
fun changeDefaultCalendarsFollowList(name: FeedDefinition) {
|
||||
changeDefaultCalendarsFollowList(name.code)
|
||||
}
|
||||
|
||||
+3
@@ -47,6 +47,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.datasource
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.datasource.FollowPacksFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.datasource.GeoHashFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.datasource.RepositoryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.GitRepositoriesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.datasource.HashtagFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.livestreams.datasource.LiveStreamsFilterAssembler
|
||||
@@ -127,6 +128,7 @@ class RelaySubscriptionsCoordinator(
|
||||
val polls = PollsFilterAssembler(client)
|
||||
val pictures = PicturesFilterAssembler(client)
|
||||
val workouts = WorkoutsFilterAssembler(client)
|
||||
val gitRepositories = GitRepositoriesFilterAssembler(client)
|
||||
val calendars = CalendarsFilterAssembler(client)
|
||||
val products = ProductsFilterAssembler(client)
|
||||
val shorts = ShortsFilterAssembler(client)
|
||||
@@ -177,6 +179,7 @@ class RelaySubscriptionsCoordinator(
|
||||
polls,
|
||||
pictures,
|
||||
workouts,
|
||||
gitRepositories,
|
||||
calendars,
|
||||
products,
|
||||
shorts,
|
||||
|
||||
@@ -66,6 +66,7 @@ object ScrollStateKeys {
|
||||
const val COMMUNITIES_LIST = "CommunitiesListFeed"
|
||||
const val PICTURES_SCREEN = "PicturesFeed"
|
||||
const val WORKOUTS_SCREEN = "WorkoutsFeed"
|
||||
const val GIT_REPOSITORIES_SCREEN = "GitRepositoriesFeed"
|
||||
const val CALENDARS_SCREEN = "CalendarsFeed"
|
||||
const val CALENDAR_COLLECTIONS_SCREEN = "CalendarCollectionsFeed"
|
||||
const val PRODUCTS_SCREEN = "ProductsFeed"
|
||||
|
||||
@@ -134,6 +134,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.FollowPack
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.GeoHashPostScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.GeoHashScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.GitRepositoryScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.GitRepositoriesScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagPostScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.HomeScreen
|
||||
@@ -323,6 +324,7 @@ fun BuildNavigation(
|
||||
composableFromBottomArgs<Route.AwardBadge> { AwardBadgeScreen(it.kind, it.pubKeyHex, it.dTag, accountViewModel, nav) }
|
||||
composableFromEnd<Route.Pictures> { PicturesScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Workouts> { WorkoutsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.GitRepositories> { GitRepositoriesScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.SoftwareApps> { SoftwareAppsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Napplets> { NappletsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Nsites> { NsitesScreen(accountViewModel, nav) }
|
||||
|
||||
+9
@@ -52,6 +52,7 @@ enum class NavBarItem {
|
||||
ARTICLES,
|
||||
PICTURES,
|
||||
WORKOUTS,
|
||||
GIT_REPOSITORIES,
|
||||
SOFTWARE_APPS,
|
||||
NAPPLETS,
|
||||
NSITES,
|
||||
@@ -219,6 +220,13 @@ val NavBarCatalog: Map<NavBarItem, NavBarItemDef> =
|
||||
icon = MaterialSymbols.DirectionsRun,
|
||||
resolveRoute = { Route.Workouts },
|
||||
),
|
||||
NavBarItem.GIT_REPOSITORIES to
|
||||
NavBarItemDef(
|
||||
id = NavBarItem.GIT_REPOSITORIES,
|
||||
labelRes = R.string.git_repositories,
|
||||
icon = MaterialSymbols.Code,
|
||||
resolveRoute = { Route.GitRepositories },
|
||||
),
|
||||
NavBarItem.SOFTWARE_APPS to
|
||||
NavBarItemDef(
|
||||
id = NavBarItem.SOFTWARE_APPS,
|
||||
@@ -426,6 +434,7 @@ val DrawerFeedsItems: List<NavBarItem> =
|
||||
NavBarItem.POLLS,
|
||||
NavBarItem.PRODUCTS,
|
||||
NavBarItem.WORKOUTS,
|
||||
NavBarItem.GIT_REPOSITORIES,
|
||||
NavBarItem.LIVE_STREAMS,
|
||||
NavBarItem.NESTS,
|
||||
NavBarItem.COMMUNITIES,
|
||||
|
||||
@@ -85,6 +85,8 @@ sealed class Route {
|
||||
|
||||
@Serializable object Workouts : Route()
|
||||
|
||||
@Serializable object GitRepositories : Route()
|
||||
|
||||
@Serializable object SoftwareApps : Route()
|
||||
|
||||
@Serializable object Napplets : Route()
|
||||
|
||||
+8
-1
@@ -41,7 +41,9 @@ import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.MemorySnapshot
|
||||
import com.vitorpamplona.amethyst.collectMemorySnapshot
|
||||
import com.vitorpamplona.amethyst.isDebug
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
fun MemoryUsageChip() {
|
||||
@@ -52,7 +54,12 @@ fun MemoryUsageChip() {
|
||||
|
||||
val snapshot by produceState<MemorySnapshot?>(null) {
|
||||
while (true) {
|
||||
value = collectMemorySnapshot(context)
|
||||
// collectMemorySnapshot reads coil3.disk.DiskLruCache.size(), which is @Synchronized and
|
||||
// contends with the disk cache's own journal I/O. On cold start that lock is held by a
|
||||
// background worker for seconds (initial journal read + the burst of image writes), so
|
||||
// running this on the produceState default (main) dispatcher froze the UI thread —
|
||||
// the "Loading account" frame couldn't repaint until size() returned. Collect off-main.
|
||||
value = withContext(Dispatchers.IO) { collectMemorySnapshot(context) }
|
||||
delay(2_000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -730,6 +731,8 @@ private fun RenderGitRepositoryEvent(
|
||||
val summary = noteEvent.description()
|
||||
val web = noteEvent.web()
|
||||
val clone = noteEvent.clone()
|
||||
val topics = remember(noteEvent) { noteEvent.hashtags().filter { it.isNotBlank() } }
|
||||
val isPersonalFork = remember(noteEvent) { noteEvent.isPersonalFork() }
|
||||
|
||||
GitCardContainer {
|
||||
Row(
|
||||
@@ -759,6 +762,15 @@ private fun RenderGitRepositoryEvent(
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
if (isPersonalFork) {
|
||||
TypeChip(
|
||||
text = stringRes(id = R.string.git_repo_personal_fork),
|
||||
background = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
symbol = MaterialSymbols.AltRoute,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
summary?.let {
|
||||
@@ -791,5 +803,21 @@ private fun RenderGitRepositoryEvent(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (topics.isNotEmpty()) {
|
||||
Spacer(modifier = HalfDoubleVertSpacer)
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
verticalArrangement = Arrangement.spacedBy(Size5dp),
|
||||
) {
|
||||
topics.forEach { topic ->
|
||||
TypeChip(
|
||||
text = "#$topic",
|
||||
background = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.06f),
|
||||
contentColor = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -47,6 +47,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip99Classifieds.D
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.drafts.dal.DraftEventsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.emojipacks.browse.dal.BrowseEmojiSetsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.dal.FollowPacksFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.dal.GitRepositoriesFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal.HomeConversationsFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal.HomeEverythingFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal.HomeLiveFilter
|
||||
@@ -111,6 +112,7 @@ class AccountFeedContentStates(
|
||||
|
||||
val picturesFeed = FeedContentState(PictureFeedFilter(account), scope, LocalCache)
|
||||
val workoutsFeed = FeedContentState(WorkoutFeedFilter(account), scope, LocalCache)
|
||||
val gitRepositoriesFeed = FeedContentState(GitRepositoriesFeedFilter(account), scope, LocalCache)
|
||||
val calendarAppointmentsFeed = FeedContentState(CalendarAppointmentsFeedFilter(account), scope, LocalCache)
|
||||
val calendarCollectionsFeed = FeedContentState(CalendarCollectionsFeedFilter(account), scope, LocalCache)
|
||||
val productsFeed = FeedContentState(ProductsFeedFilter(account), scope, LocalCache)
|
||||
@@ -228,6 +230,7 @@ class AccountFeedContentStates(
|
||||
|
||||
picturesFeed.updateFeedWith(newNotes)
|
||||
workoutsFeed.updateFeedWith(newNotes)
|
||||
gitRepositoriesFeed.updateFeedWith(newNotes)
|
||||
productsFeed.updateFeedWith(newNotes)
|
||||
shortsFeed.updateFeedWith(newNotes)
|
||||
publicChatsFeed.updateFeedWith(newNotes)
|
||||
@@ -289,6 +292,7 @@ class AccountFeedContentStates(
|
||||
|
||||
picturesFeed.deleteFromFeed(newNotes)
|
||||
workoutsFeed.deleteFromFeed(newNotes)
|
||||
gitRepositoriesFeed.deleteFromFeed(newNotes)
|
||||
productsFeed.deleteFromFeed(newNotes)
|
||||
shortsFeed.deleteFromFeed(newNotes)
|
||||
publicChatsFeed.deleteFromFeed(newNotes)
|
||||
@@ -346,6 +350,7 @@ class AccountFeedContentStates(
|
||||
|
||||
picturesFeed.trimToSize(maxItems)
|
||||
workoutsFeed.trimToSize(maxItems)
|
||||
gitRepositoriesFeed.trimToSize(maxItems)
|
||||
calendarAppointmentsFeed.trimToSize(maxItems)
|
||||
calendarCollectionsFeed.trimToSize(maxItems)
|
||||
productsFeed.trimToSize(maxItems)
|
||||
|
||||
+3
@@ -34,6 +34,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.list.datasource
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource.DiscoveryFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.emojipacks.browse.datasource.BrowseEmojiSetsFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.datasource.FollowPacksFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.GitRepositoriesFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.livestreams.datasource.LiveStreamsFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.longs.datasource.LongsFilterAssemblerSubscription
|
||||
@@ -96,6 +97,8 @@ private fun PreloadFor(
|
||||
|
||||
NavBarItem.WORKOUTS -> WorkoutsFilterAssemblerSubscription(accountViewModel)
|
||||
|
||||
NavBarItem.GIT_REPOSITORIES -> GitRepositoriesFilterAssemblerSubscription(accountViewModel)
|
||||
|
||||
NavBarItem.SOFTWARE_APPS -> SoftwareAppsFilterAssemblerSubscription(accountViewModel)
|
||||
|
||||
// Napplets & nSites read directly from the local cache; their screens open the discovery
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.ui.feeds.RenderFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.SaveableFeedContentState
|
||||
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.GitRepositoriesFilterAssemblerSubscription
|
||||
|
||||
@Composable
|
||||
fun GitRepositoriesScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
GitRepositoriesScreen(
|
||||
gitRepositoriesFeedContentState = accountViewModel.feedStates.gitRepositoriesFeed,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GitRepositoriesScreen(
|
||||
gitRepositoriesFeedContentState: FeedContentState,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
WatchLifecycleAndUpdateModel(gitRepositoriesFeedContentState)
|
||||
WatchAccountForGitRepositoriesScreen(gitRepositoriesFeedContentState = gitRepositoriesFeedContentState, accountViewModel = accountViewModel)
|
||||
GitRepositoriesFilterAssemblerSubscription(accountViewModel)
|
||||
|
||||
DisappearingScaffold(
|
||||
isInvertedLayout = false,
|
||||
topBar = {
|
||||
GitRepositoriesTopBar(accountViewModel, nav)
|
||||
},
|
||||
bottomBar = {
|
||||
AppBottomBar(Route.GitRepositories, nav, accountViewModel) { route ->
|
||||
if (route == Route.GitRepositories) {
|
||||
gitRepositoriesFeedContentState.sendToTop()
|
||||
} else {
|
||||
nav.navBottomBar(route)
|
||||
}
|
||||
}
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
RefresheableBox(gitRepositoriesFeedContentState, true) {
|
||||
SaveableFeedContentState(gitRepositoriesFeedContentState, scrollStateKey = ScrollStateKeys.GIT_REPOSITORIES_SCREEN) { listState ->
|
||||
RenderFeedContentState(
|
||||
feedContentState = gitRepositoriesFeedContentState,
|
||||
accountViewModel = accountViewModel,
|
||||
listState = listState,
|
||||
nav = nav,
|
||||
routeForLastRead = "GitRepositoriesFeed",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WatchAccountForGitRepositoriesScreen(
|
||||
gitRepositoriesFeedContentState: FeedContentState,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val listState by accountViewModel.account.liveGitRepositoriesFollowLists.collectAsStateWithLifecycle()
|
||||
val hiddenUsers =
|
||||
accountViewModel.account.hiddenUsers.flow
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(accountViewModel, listState, hiddenUsers) {
|
||||
gitRepositoriesFeedContentState.checkKeysInvalidateDataAndSendToTop()
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.TopFilter
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.FeedFilterSpinner
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.UserDrawerSearchTopBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.FeedDefinition
|
||||
import com.vitorpamplona.amethyst.ui.screen.TopNavFilterState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
@Composable
|
||||
fun GitRepositoriesTopBar(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
UserDrawerSearchTopBar(accountViewModel, nav) {
|
||||
val list by accountViewModel.account.settings.defaultGitRepositoriesFollowList
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
GitRepositoriesTopNavFilterBar(
|
||||
followListsModel = accountViewModel.feedStates.feedListOptions,
|
||||
listName = list,
|
||||
accountViewModel = accountViewModel,
|
||||
onChange = accountViewModel.account.settings::changeDefaultGitRepositoriesFollowList,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GitRepositoriesTopNavFilterBar(
|
||||
followListsModel: TopNavFilterState,
|
||||
listName: TopFilter,
|
||||
accountViewModel: AccountViewModel,
|
||||
onChange: (FeedDefinition) -> Unit,
|
||||
) {
|
||||
val allLists by followListsModel.kind3GlobalPeopleRoutes.collectAsStateWithLifecycle()
|
||||
|
||||
FeedFilterSpinner(
|
||||
placeholderCode = listName,
|
||||
explainer = stringRes(R.string.select_list_to_filter),
|
||||
options = allLists,
|
||||
onSelect = onChange,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.TopFilter
|
||||
import com.vitorpamplona.amethyst.model.filterIntoSet
|
||||
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
|
||||
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
|
||||
class GitRepositoriesFeedFilter(
|
||||
val account: Account,
|
||||
) : AdditiveFeedFilter<Note>() {
|
||||
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + followList().code
|
||||
|
||||
override fun limit() = 200
|
||||
|
||||
fun followList(): TopFilter = account.settings.defaultGitRepositoriesFollowList.value
|
||||
|
||||
fun TopFilter.isMuteList() = this is TopFilter.MuteList
|
||||
|
||||
fun TopFilter.isBlockList() = this is TopFilter.PeopleList && this.address == account.blockPeopleList.getBlockListAddress()
|
||||
|
||||
fun TopFilter.wantsToSeeNegativeStuff() = isMuteList() || isBlockList()
|
||||
|
||||
override fun showHiddenKey(): Boolean = followList().wantsToSeeNegativeStuff()
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
|
||||
val notes =
|
||||
LocalCache.addressables.filterIntoSet(GitRepositoryEvent.KIND) { _, it ->
|
||||
val noteEvent = it.event
|
||||
noteEvent is GitRepositoryEvent && params.match(noteEvent, it.relays)
|
||||
}
|
||||
|
||||
return sort(notes)
|
||||
}
|
||||
|
||||
override fun applyFilter(newItems: Set<Note>): Set<Note> = innerApplyFilter(newItems)
|
||||
|
||||
fun buildFilterParams(account: Account): FilterByListParams =
|
||||
FilterByListParams.create(
|
||||
account.liveGitRepositoriesFollowLists.value,
|
||||
account.hiddenUsers.flow.value,
|
||||
)
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val params = buildFilterParams(account)
|
||||
|
||||
return collection.filterTo(HashSet()) {
|
||||
val noteEvent = it.event
|
||||
noteEvent is GitRepositoryEvent && params.match(noteEvent, it.relays)
|
||||
}
|
||||
}
|
||||
|
||||
override fun sort(items: Set<Note>): List<Note> = items.sortedByDefaultFeedOrder()
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.aroundMe.LocationTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.global.GlobalTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.hashtag.HashtagTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.allcommunities.AllCommunitiesTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.community.SingleCommunityTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.muted.MutedAuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesByAllCommunities
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesByAuthors
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesByCommunity
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesByFollows
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesByGeohashes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesByHashtag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesByMutedAuthors
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesGlobal
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
fun makeGitRepositoriesFilter(
|
||||
feedSettings: IFeedTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> =
|
||||
when (feedSettings) {
|
||||
is AllCommunitiesTopNavPerRelayFilterSet -> filterGitRepositoriesByAllCommunities(feedSettings, since, defaultSince)
|
||||
is AllFollowsTopNavPerRelayFilterSet -> filterGitRepositoriesByFollows(feedSettings, since, defaultSince)
|
||||
is AuthorsTopNavPerRelayFilterSet -> filterGitRepositoriesByAuthors(feedSettings, since, defaultSince)
|
||||
is GlobalTopNavPerRelayFilterSet -> filterGitRepositoriesGlobal(feedSettings, since, defaultSince)
|
||||
is HashtagTopNavPerRelayFilterSet -> filterGitRepositoriesByHashtag(feedSettings, since, defaultSince)
|
||||
is LocationTopNavPerRelayFilterSet -> filterGitRepositoriesByGeohashes(feedSettings, since, defaultSince)
|
||||
is MutedAuthorsTopNavPerRelayFilterSet -> filterGitRepositoriesByMutedAuthors(feedSettings, since, defaultSince)
|
||||
is SingleCommunityTopNavPerRelayFilterSet -> filterGitRepositoriesByCommunity(feedSettings, since, defaultSince)
|
||||
else -> emptyList()
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountFeedContentStates
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
class GitRepositoriesQueryState(
|
||||
val account: Account,
|
||||
val feedStates: AccountFeedContentStates,
|
||||
val scope: CoroutineScope,
|
||||
)
|
||||
|
||||
@Stable
|
||||
class GitRepositoriesFilterAssembler(
|
||||
client: INostrClient,
|
||||
) : ComposeSubscriptionManager<GitRepositoriesQueryState>() {
|
||||
val group =
|
||||
listOf(
|
||||
GitRepositoriesSubAssembler(client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateKeys() = invalidateFilters()
|
||||
|
||||
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||
|
||||
override fun destroy() = group.forEach { it.destroy() }
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Composable
|
||||
fun GitRepositoriesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
GitRepositoriesFilterAssemblerSubscription(
|
||||
accountViewModel.dataSources().gitRepositories,
|
||||
accountViewModel,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GitRepositoriesFilterAssemblerSubscription(
|
||||
dataSource: GitRepositoriesFilterAssembler,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
GitRepositoriesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.model.TopFilter
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GitRepositoriesSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<GitRepositoriesQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<GitRepositoriesQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: GitRepositoriesQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeGitRepositoriesFilter(feedSettings, since, key.feedStates.gitRepositoriesFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: GitRepositoriesQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: GitRepositoriesQueryState) = key.listName()
|
||||
|
||||
fun GitRepositoriesQueryState.listNameFlow() = account.settings.defaultGitRepositoriesFollowList
|
||||
|
||||
fun GitRepositoriesQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun GitRepositoriesQueryState.followsPerRelayFlow() = account.liveGitRepositoriesFollowListsPerRelay
|
||||
|
||||
fun GitRepositoriesQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: GitRepositoriesQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.gitRepositoriesFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.allcommunities.AllCommunitiesTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.community.SingleCommunityTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent
|
||||
|
||||
val GitRepositoriesFromCommunityKinds =
|
||||
listOf(
|
||||
GitRepositoryEvent.KIND,
|
||||
)
|
||||
|
||||
val GitRepositoriesFromCommunityKindsStr =
|
||||
listOf(
|
||||
GitRepositoryEvent.KIND.toString(),
|
||||
)
|
||||
|
||||
fun filterGitRepositoriesFromAllCommunities(
|
||||
relay: NormalizedRelayUrl,
|
||||
communities: Set<String>,
|
||||
since: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
val communityList = communities.sorted()
|
||||
|
||||
return listOf(
|
||||
// approved
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = CommunityPostApprovalEvent.KIND_LIST,
|
||||
tags =
|
||||
mapOf(
|
||||
"a" to communityList,
|
||||
"k" to GitRepositoriesFromCommunityKindsStr,
|
||||
),
|
||||
limit = communityList.size * 20,
|
||||
since = since,
|
||||
),
|
||||
),
|
||||
// not approved
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
tags = mapOf("a" to communityList),
|
||||
kinds = GitRepositoriesFromCommunityKinds,
|
||||
limit = communityList.size * 20,
|
||||
since = since,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun filterGitRepositoriesByAllCommunities(
|
||||
communitySet: AllCommunitiesTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
if (communitySet.set.isEmpty()) return emptyList()
|
||||
|
||||
return communitySet.set
|
||||
.mapNotNull {
|
||||
filterGitRepositoriesFromAllCommunities(
|
||||
relay = it.key,
|
||||
communities = it.value.communities,
|
||||
since = since?.get(it.key)?.time ?: defaultSince,
|
||||
)
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
fun filterGitRepositoriesFromCommunity(
|
||||
relay: NormalizedRelayUrl,
|
||||
community: String,
|
||||
authors: Set<String>?,
|
||||
since: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
val authors = authors?.sorted()
|
||||
return listOf(
|
||||
// approved
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
authors = authors,
|
||||
kinds = CommunityPostApprovalEvent.KIND_LIST,
|
||||
tags =
|
||||
mapOf(
|
||||
"a" to listOf(community),
|
||||
"k" to GitRepositoriesFromCommunityKindsStr,
|
||||
),
|
||||
limit = 100,
|
||||
since = since,
|
||||
),
|
||||
),
|
||||
// not approved
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
authors = authors,
|
||||
tags = mapOf("a" to listOf(community)),
|
||||
kinds = GitRepositoriesFromCommunityKinds,
|
||||
limit = 100,
|
||||
since = since,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun filterGitRepositoriesByCommunity(
|
||||
communitySet: SingleCommunityTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
if (communitySet.set.isEmpty()) return emptyList()
|
||||
|
||||
return communitySet.set
|
||||
.mapNotNull {
|
||||
filterGitRepositoriesFromCommunity(
|
||||
relay = it.key,
|
||||
community = it.value.community,
|
||||
authors = it.value.authors,
|
||||
since = since?.get(it.key)?.time ?: defaultSince,
|
||||
)
|
||||
}.flatten()
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.muted.MutedAuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
|
||||
fun filterGitRepositoriesByAuthors(
|
||||
relay: NormalizedRelayUrl,
|
||||
authors: Set<HexKey>,
|
||||
since: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
val authorList = authors.sorted()
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
authors = authorList,
|
||||
kinds = listOf(GitRepositoryEvent.KIND),
|
||||
limit = 200,
|
||||
since = since,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun filterGitRepositoriesByAuthors(
|
||||
authorSet: AuthorsTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
if (authorSet.set.isEmpty()) return emptyList()
|
||||
|
||||
return authorSet.set
|
||||
.mapNotNull {
|
||||
if (it.value.authors.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
filterGitRepositoriesByAuthors(
|
||||
relay = it.key,
|
||||
authors = it.value.authors,
|
||||
since = since?.get(it.key)?.time ?: defaultSince,
|
||||
)
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
fun filterGitRepositoriesByMutedAuthors(
|
||||
authorSet: MutedAuthorsTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
if (authorSet.set.isEmpty()) return emptyList()
|
||||
|
||||
return authorSet.set
|
||||
.mapNotNull {
|
||||
if (it.value.authors.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
filterGitRepositoriesByAuthors(
|
||||
relay = it.key,
|
||||
authors = it.value.authors,
|
||||
since = since?.get(it.key)?.time ?: defaultSince,
|
||||
)
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
fun filterGitRepositoriesByFollows(
|
||||
followsSet: AllFollowsTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
if (followsSet.set.isEmpty()) return emptyList()
|
||||
|
||||
return followsSet.set.flatMap {
|
||||
val since = since?.get(it.key)?.time ?: defaultSince
|
||||
val relay = it.key
|
||||
|
||||
listOfNotNull(
|
||||
it.value.authors?.let {
|
||||
filterGitRepositoriesByAuthors(relay, it, since)
|
||||
},
|
||||
).flatten()
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.aroundMe.LocationTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
|
||||
fun filterGitRepositoriesByGeohashes(
|
||||
relay: NormalizedRelayUrl,
|
||||
geotags: Set<String>,
|
||||
since: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (geotags.isEmpty()) return emptyList()
|
||||
|
||||
return listOf(
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(GitRepositoryEvent.KIND),
|
||||
tags = mapOf("g" to geotags.sorted()),
|
||||
limit = 100,
|
||||
since = since,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun filterGitRepositoriesByGeohashes(
|
||||
geoSet: LocationTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (geoSet.set.isEmpty()) return emptyList()
|
||||
|
||||
return geoSet.set
|
||||
.mapNotNull {
|
||||
if (it.value.geotags.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
filterGitRepositoriesByGeohashes(
|
||||
relay = it.key,
|
||||
geotags = it.value.geotags,
|
||||
since = since?.get(it.key)?.time ?: defaultSince,
|
||||
)
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.hashtag.HashtagTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
|
||||
fun filterGitRepositoriesByHashtag(
|
||||
relay: NormalizedRelayUrl,
|
||||
hashtags: Set<String>,
|
||||
since: Long? = null,
|
||||
): List<RelayBasedFilter> =
|
||||
listOf(
|
||||
RelayBasedFilter(
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(GitRepositoryEvent.KIND),
|
||||
tags = mapOf("t" to hashtags.toList()),
|
||||
limit = 200,
|
||||
since = since,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
fun filterGitRepositoriesByHashtag(
|
||||
hashtagSet: HashtagTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
if (hashtagSet.set.isEmpty()) return emptyList()
|
||||
|
||||
return hashtagSet.set
|
||||
.mapNotNull { relayHashSet ->
|
||||
if (relayHashSet.value.hashtags.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
filterGitRepositoriesByHashtag(
|
||||
relay = relayHashSet.key,
|
||||
hashtags = relayHashSet.value.hashtags,
|
||||
since = since?.get(relayHashSet.key)?.time ?: defaultSince,
|
||||
)
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.topNavFeeds.global.GlobalTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
fun filterGitRepositoriesGlobal(
|
||||
relays: GlobalTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> {
|
||||
if (relays.set.isEmpty()) return emptyList()
|
||||
|
||||
return relays.set.map {
|
||||
val since = since?.get(it.key)?.time ?: defaultSince ?: TimeUtils.oneWeekAgo()
|
||||
RelayBasedFilter(
|
||||
relay = it.key,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(GitRepositoryEvent.KIND),
|
||||
limit = 200,
|
||||
since = since,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -719,6 +719,7 @@
|
||||
<item quantity="other">Este nApplet quiere pagar una factura Lightning de %1$d sats.</item>
|
||||
</plurals>
|
||||
<string name="nip82_repository_label">Fuente: %1$s</string>
|
||||
<string name="nip82_version_label">v%1$s</string>
|
||||
<string name="nip82_download">Descargar</string>
|
||||
<string name="nip82_section_about">Acerca de</string>
|
||||
<string name="nip82_section_platforms">Plataformas</string>
|
||||
@@ -878,6 +879,7 @@
|
||||
<string name="route_music_tracks">Música</string>
|
||||
<string name="route_music_playlists">Listas de reproducción</string>
|
||||
<string name="route_podcast_episodes">Episodios</string>
|
||||
<string name="route_podcasts">Podcasts</string>
|
||||
<string name="podcast_view_episodes">Ver episodios</string>
|
||||
<string name="podcast_no_episodes">Aún no se han encontrado episodios</string>
|
||||
<plurals name="podcast_episode_count">
|
||||
@@ -915,6 +917,7 @@
|
||||
<string name="add_hashtag_label">Añadir hashtag</string>
|
||||
<string name="add_hashtag_label_title">Añadir un hashtag</string>
|
||||
<string name="add_hashtag_label_explainer">Etiqueta públicamente esta publicación con un hashtag (etiqueta NIP-32). Las personas que te siguen lo verán en el feed de ese hashtag.</string>
|
||||
<string name="add_hashtag_label_field">Hashtag</string>
|
||||
<string name="add_hashtag_label_confirm">Añadir</string>
|
||||
<string name="hashtag_label_added_by">añadido por</string>
|
||||
<string name="pinned_notes">Notas fijadas</string>
|
||||
@@ -941,6 +944,7 @@
|
||||
<string name="interest_set_name_placeholder">Mis intereses</string>
|
||||
<string name="interest_set_hashtag_add_placeholder">Añadir hashtag</string>
|
||||
<string name="interest_set_hashtag_private_toggle">Privado</string>
|
||||
<string name="interest_set_hashtag_count">%1$d hashtag(s)</string>
|
||||
<string name="interest_set_actions_dialog_title">Acciones del conjunto de intereses</string>
|
||||
<string name="interest_set_rename">Renombrar</string>
|
||||
<string name="interest_set_clone">Clonar</string>
|
||||
@@ -980,6 +984,7 @@
|
||||
<string name="quick_zap_amounts_explainer">Aparece al presionar el botón de zap. Pulsa una cantidad para eliminarla. Si la dejas vacía, se abrirá el cuadro de diálogo para introducir una cantidad cada vez.</string>
|
||||
<string name="send_onchain_instead">Enviar en cadena en su lugar</string>
|
||||
<string name="reload_mint_title">Recargar mint</string>
|
||||
<string name="reload_mint_sats_amount">%1$s sats</string>
|
||||
<string name="reload_mint_topup_label">Importe de recarga</string>
|
||||
<string name="reload_mint_section_to">Mint a recargar</string>
|
||||
<string name="reload_mint_section_from">Fondos de</string>
|
||||
@@ -1297,6 +1302,7 @@
|
||||
<string name="hide_community_rules_violations_explainer">Elimina publicaciones de los feeds de comunidades cuando la comunidad publica un documento de normas NIP-9B y un evento no lo cumple. No tiene efecto cuando una comunidad no tiene normas estructuradas.</string>
|
||||
<string name="security_section_filtering_preferences">Preferencias de filtrado</string>
|
||||
<string name="security_section_blocked_content">Contenido bloqueado</string>
|
||||
<string name="security_unlimited">∞</string>
|
||||
<string name="security_blocked_users_empty">Aún no has bloqueado a ningún usuario.</string>
|
||||
<string name="security_spamming_users_empty">Ninguna cuenta ha sido marcada como spam en esta sesión.</string>
|
||||
<string name="security_hidden_words_empty">No hay palabras ocultas. Añade una palabra a continuación para ocultar las publicaciones que la contengan.</string>
|
||||
@@ -1355,6 +1361,7 @@
|
||||
<string name="relay_count_subscriptions">Suscripciones COUNT (%1$d)</string>
|
||||
<string name="relay_outbox_events">Eventos de salida (%1$d)</string>
|
||||
<string name="relay_filter_authors">%1$d autores</string>
|
||||
<string name="relay_filter_ids">%1$d ids</string>
|
||||
<string name="relay_filter_since">desde %1$s</string>
|
||||
<string name="relay_filter_until">hasta %1$s</string>
|
||||
<string name="relay_filter_limit">límite %1$d</string>
|
||||
@@ -1365,6 +1372,7 @@
|
||||
<string name="accepts_up_to">Acepta hasta</string>
|
||||
<string name="time_in_the_future">%1$s en el futuro</string>
|
||||
<string name="time_in_the_past">hace %1$s</string>
|
||||
<string name="amount_in_bits">%1$s bits</string>
|
||||
<string name="event_retention">Retención de eventos</string>
|
||||
<string name="content_size">Tamaño de contenido</string>
|
||||
<string name="connectivity">Conectividad</string>
|
||||
@@ -1378,6 +1386,7 @@
|
||||
<string name="max_limit_events_returning">Límite máximo (devolución de eventos)</string>
|
||||
<string name="max_subid_length">Longitud máxima de subID</string>
|
||||
<string name="cashu">Token de Cashu</string>
|
||||
<string name="cashu_mint_label">Mint: %1$s</string>
|
||||
<string name="cashu_redeem">Canjear</string>
|
||||
<string name="cashu_copy_token">Copiar token</string>
|
||||
<string name="quick_actions_open_in_another_app">Abrir en otra app</string>
|
||||
@@ -1735,6 +1744,7 @@
|
||||
<string name="wallet_outgoing">Enviado</string>
|
||||
<string name="wallet_refresh">Actualizar</string>
|
||||
<string name="wallet_filter_all">Todos</string>
|
||||
<string name="wallet_filter_zaps">Zaps</string>
|
||||
<string name="wallet_filter_non_zaps">No-Zaps</string>
|
||||
<string name="wallet_add">Añadir cartera</string>
|
||||
<string name="wallet_default">Predeterminada</string>
|
||||
@@ -1758,10 +1768,13 @@
|
||||
<string name="cashu_discovering">Buscando tu monedero…</string>
|
||||
<string name="cashu_discovering_description">Los monederos NIP-60 se sincronizan entre clientes. Si creaste uno en otra aplicación con esta clave Nostr, debería aparecer en unos segundos.</string>
|
||||
<string name="cashu_balance">Saldo</string>
|
||||
<string name="cashu_mints">Mints</string>
|
||||
<string name="cashu_mint_url">URL del mint</string>
|
||||
<string name="cashu_remove_mint">Eliminar mint</string>
|
||||
<string name="cashu_add_mint">Añadir mint</string>
|
||||
<string name="cashu_history">Historial</string>
|
||||
<string name="cashu_wallet_autosaves">Tu monedero guarda automáticamente a medida que añades o eliminas mints. Se crea una clave nutzap la primera vez que añades una mint.</string>
|
||||
<string name="cashu_wallet_saving">Guardando…</string>
|
||||
<string name="cashu_p2pk_section">Clave nutzap (avanzado)</string>
|
||||
<string name="cashu_p2pk_explainer">Una clave privada separada usada únicamente para recibir nutzaps NIP-61. No es tu clave de identidad Nostr.</string>
|
||||
<string name="cashu_p2pk_autogen">Generar una nueva clave</string>
|
||||
@@ -1834,7 +1847,9 @@
|
||||
<string name="cashu_quote_confirm">¿Pagar %1$s sat + hasta %2$s sat en comisiones?</string>
|
||||
<string name="cashu_verify">Verificar</string>
|
||||
<string name="cashu_mint_reachable">✓ El mint es accesible</string>
|
||||
<string name="cashu_mint_reachable_named">✓ %1$s</string>
|
||||
<string name="cashu_mint_unreachable">No se pudo alcanzar el mint: %1$s</string>
|
||||
<string name="nutzap">Nutzap</string>
|
||||
<string name="nutzap_failed_title">Nutzap fallido</string>
|
||||
<string name="nutzap_failed_no_recipient">No hay clave pública de destinatario en la nota</string>
|
||||
<string name="nutzap_failed_private_note">Los nutzaps son públicos y revelarían esta nota privada. Usa un zap Lightning en su lugar.</string>
|
||||
@@ -1876,12 +1891,18 @@
|
||||
<string name="onchain_send_recipient_placeholder">nombre, NIP-05 o npub</string>
|
||||
<string name="onchain_send_change_recipient">Cambiar destinatario</string>
|
||||
<string name="onchain_send_amount">Cantidad</string>
|
||||
<string name="onchain_send_sats_suffix">sats</string>
|
||||
<string name="onchain_send_min_warning">El zap onchain mínimo es %1$s sats — las cantidades menores son consumidas por las comisiones de minería. Usa un zap Lightning en su lugar.</string>
|
||||
<string name="onchain_send_comment_label">Comentario (opcional)</string>
|
||||
<string name="onchain_send_priority">Prioridad</string>
|
||||
<string name="onchain_send_fee_slow">Lento</string>
|
||||
<string name="onchain_send_fee_normal">Normal</string>
|
||||
<string name="onchain_send_fee_fast">Rápido</string>
|
||||
<string name="onchain_send_fee_eta_slow">~1 h</string>
|
||||
<string name="onchain_send_fee_eta_normal">~30 min</string>
|
||||
<string name="onchain_send_fee_eta_fast">~10 min</string>
|
||||
<string name="onchain_send_fee_rate_eta">%1$s sat/vB · %2$s</string>
|
||||
<string name="onchain_send_fee_tier_label_rate_eta">%1$s · %2$s sat/vB · %3$s</string>
|
||||
<string name="onchain_send_loading_fees">Cargando estimaciones de comisión…</string>
|
||||
<string name="onchain_send_button_amount">Enviar %1$s sats</string>
|
||||
<plurals name="onchain_send_button_amount_split">
|
||||
@@ -1906,6 +1927,7 @@
|
||||
<string name="onchain_send_result_transaction">Transacción</string>
|
||||
<string name="onchain_send_result_fee">Comisión</string>
|
||||
<string name="onchain_send_result_change">Cambio</string>
|
||||
<string name="onchain_send_sats_amount">%1$s sats</string>
|
||||
<string name="onchain_send_done">Hecho</string>
|
||||
<string name="onchain_send_failed_at">Error en: %1$s</string>
|
||||
<string name="onchain_send_broadcast_no_receipt">El pago se difundió (tx %1$s) pero el recibo no se publicó.</string>
|
||||
@@ -1940,6 +1962,7 @@
|
||||
<string name="edit_calendar_collection">Editar calendario</string>
|
||||
<string name="calendar_collection_events_section">Eventos en este calendario (%1$d)</string>
|
||||
<string name="calendar_collection_no_events_yet">Aún no has creado ningún evento de calendario.</string>
|
||||
<string name="calendar_view_feed">Tablón</string>
|
||||
<string name="calendar_view_month">Mes</string>
|
||||
<string name="calendar_view_week">Semana</string>
|
||||
<string name="calendar_view_day">Día</string>
|
||||
@@ -2001,6 +2024,7 @@
|
||||
<string name="calendar_rsvp_going">Asistiré</string>
|
||||
<string name="calendar_rsvp_maybe">Tal vez</string>
|
||||
<string name="calendar_rsvp_not_going">No puedo ir</string>
|
||||
<string name="calendar_rsvp_section">Confirmaciones (%1$d)</string>
|
||||
<string name="calendar_rsvp_none">Sin RSVPs aún.</string>
|
||||
<string name="calendar_participants_section">Participantes (%1$d)</string>
|
||||
<string name="calendar_event_in_calendars">En calendarios (%1$d)</string>
|
||||
@@ -2010,6 +2034,7 @@
|
||||
<string name="calendar_relative_ongoing_with_end">%1$s · termina %2$s</string>
|
||||
<string name="calendar_export_share_title">Compartir evento de calendario</string>
|
||||
<string name="calendar_export_event">Exportar al calendario (.ics)</string>
|
||||
<string name="calendar_reminder_channel_id">recordatorios_calendario</string>
|
||||
<string name="calendar_reminder_channel_name">Recordatorios de calendario</string>
|
||||
<string name="calendar_reminder_channel_description">Aviso cuando un evento al que asistirás está a punto de comenzar.</string>
|
||||
<string name="calendar_reminder_default_title">Evento de calendario</string>
|
||||
@@ -2034,6 +2059,10 @@
|
||||
<string name="calendar_reminder_settings_enabled_subtitle">Se enviará una notificación cuando un evento al que asistirás esté a punto de comenzar.</string>
|
||||
<string name="calendar_reminder_settings_lead_title">Tiempo de antelación del recordatorio</string>
|
||||
<string name="calendar_reminder_settings_lead_subtitle">Cuántos minutos antes del evento quieres recibir la notificación.</string>
|
||||
<plurals name="calendar_reminder_settings_lead_choice">
|
||||
<item quantity="one">%1$d min</item>
|
||||
<item quantity="other">%1$d min</item>
|
||||
</plurals>
|
||||
<string name="calendar_share_nostr">Compartir como enlace Nostr</string>
|
||||
<string name="calendar_share_nostr_title">Compartir enlace de calendario</string>
|
||||
<string name="calendar_filter_all">Todos los calendarios</string>
|
||||
@@ -2182,6 +2211,7 @@
|
||||
<string name="private_inbox_section">Relés de entrada DM</string>
|
||||
<string name="private_inbox_section_explainer_profile">El usuario recibe mensajes directos en estos relés</string>
|
||||
<string name="private_inbox_section_explainer">Inserta entre 1 y 3 relés para que te sirvan de buzón de entrada privado. Otros usarán estos relés para enviarte mensajes directos. Los relés del buzón de entrada para mensajes directos deberían aceptar cualquier mensaje de cualquier persona, pero solo te permiten descargarlos. Algunas buenas opciones son:\n - inbox.nostr.wine (de pago)\n - you.nostr1.com (relés personales; de pago)</string>
|
||||
<string name="keypackages">Paquetes de claves</string>
|
||||
<string name="keypackage_section">Relays de KeyPackage</string>
|
||||
<string name="keypackage_section_explainer">Relays donde se publican tus MLS KeyPackages (MIP-00). Otros usuarios obtienen estas KeyPackages para invitarte a chats de grupo Marmot. Introduce entre 1 y 3 relays que acepten eventos KeyPackage de tu parte y permitan lecturas públicas.</string>
|
||||
<string name="private_outbox_section">Relés privados</string>
|
||||
@@ -2226,17 +2256,27 @@
|
||||
<string name="forked_from">Bifurcación de</string>
|
||||
<string name="git_web_address">Web:</string>
|
||||
<string name="git_clone_address">Clon:</string>
|
||||
<string name="git_branch">Rama</string>
|
||||
<string name="git_commit">Confirmar</string>
|
||||
<string name="git_merge_base">Fusionar base</string>
|
||||
<string name="git_pr_update_description">Se actualizó la solicitud de incorporación de cambios con una nueva confirmación.</string>
|
||||
<string name="git_status_open">Abierto</string>
|
||||
<string name="git_status_merged">Fusionado</string>
|
||||
<string name="git_status_closed">Cerrado</string>
|
||||
<string name="git_status_draft">Borrador</string>
|
||||
<string name="git_repo_tab_overview">Resumen</string>
|
||||
<string name="git_repo_tab_issues">Incidencias</string>
|
||||
<string name="git_repo_tab_patches">Parches y PRs</string>
|
||||
<string name="git_repo_filter_open">Abierto</string>
|
||||
<string name="git_repo_filter_closed">Cerrado y resuelto</string>
|
||||
<string name="git_untitled">Sin título</string>
|
||||
<string name="git_repo_section_about">Acerca de</string>
|
||||
<string name="git_repo_section_links">Enlaces</string>
|
||||
<string name="git_repo_section_maintainers">Mantenedores</string>
|
||||
<string name="git_repo_section_topics">Temas</string>
|
||||
<string name="git_repo_personal_fork">Fork personal</string>
|
||||
<string name="nsite_title">nSite: %1$s</string>
|
||||
<string name="napplet_card_title">nApplet: %1$s</string>
|
||||
<string name="napplet_card_permissions">Permisos:</string>
|
||||
<string name="profile_tab_apps">Apps y sitios</string>
|
||||
<string name="nsite_root_site">Sitio raíz</string>
|
||||
@@ -2310,12 +2350,15 @@
|
||||
<string name="torrent_no_info">El evento no tiene suficiente información para crear un enlace magnético</string>
|
||||
<string name="my_lists">Mis listas</string>
|
||||
<string name="select_list_to_filter">Selecciona una lista para filtrar el tablón</string>
|
||||
<string name="feed_group_feeds">Tablones</string>
|
||||
<string name="feed_group_hashtags">Hashtags</string>
|
||||
<string name="feed_group_interest_sets">Conjuntos de intereses</string>
|
||||
<string name="feed_group_locations">Ubicaciones</string>
|
||||
<string name="feed_group_communities">Comunidades</string>
|
||||
<string name="feed_group_lists">Listas</string>
|
||||
<string name="feed_group_dvms">Algoritmos de feed</string>
|
||||
<string name="follow_list_all_favorite_dvms">Todos los algoritmos de feed favoritos</string>
|
||||
<string name="feed_group_relays">Relés</string>
|
||||
<string name="add_dvm_to_favorites">Añadir algoritmo de feed a favoritos</string>
|
||||
<string name="remove_dvm_from_favorites">Eliminar de favoritos</string>
|
||||
<string name="favorite_dvms_title">Algoritmos de feed favoritos</string>
|
||||
@@ -2345,10 +2388,13 @@
|
||||
<string name="search_scope_all">Todo</string>
|
||||
<string name="search_scope_people">Personas</string>
|
||||
<string name="search_scope_notes">Notas</string>
|
||||
<string name="search_source_local">Local</string>
|
||||
<string name="search_source_relays">Relés</string>
|
||||
<string name="search_follows_only">Solo seguidos</string>
|
||||
<string name="search_sort_newest">Más recientes</string>
|
||||
<string name="search_sort_oldest">Más antiguos</string>
|
||||
<string name="search_sort_relevance">Relevancia</string>
|
||||
<string name="search_sort_popular">Popular</string>
|
||||
<string name="search_filters_title">Filtros</string>
|
||||
<string name="search_filters_reset">Restablecer</string>
|
||||
<string name="search_filters_section_source">Fuente</string>
|
||||
@@ -2360,6 +2406,7 @@
|
||||
<string name="translate_to_description">Elige el idioma al que traducir el contenido.</string>
|
||||
<string name="language_preferences">Preferencias de visualización de idioma</string>
|
||||
<string name="language_preferences_description">Para cada par de idiomas traducidos, elige qué idioma mostrar primero.</string>
|
||||
<string name="language_preference_pair">%1$s → %2$s</string>
|
||||
<string name="search_languages">Buscar idiomas</string>
|
||||
<string name="add_language">Añadir idioma</string>
|
||||
<string name="add_language_pair">Añadir par de idiomas</string>
|
||||
@@ -2416,6 +2463,7 @@
|
||||
<string name="retrying">Reintentando…</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="dismiss">Descartar</string>
|
||||
<string name="share_of">%1$d/%2$d</string>
|
||||
<string name="broadcasting">Difundiendo</string>
|
||||
<string name="broadcasting_name">Difundiendo %1$s</string>
|
||||
<string name="broadcasting_number_events">Difundiendo %1$d eventos…</string>
|
||||
@@ -2512,10 +2560,13 @@
|
||||
<string name="kind_follow_packs">Packs de seguidos</string>
|
||||
<string name="kind_reposts_16">Republicaciones (16)</string>
|
||||
<string name="kind_geohash_follows">Seguidos por geohash</string>
|
||||
<string name="kind_gift_wraps">GiftWraps</string>
|
||||
<string name="kind_git_issue">Incidencia de Git</string>
|
||||
<string name="kind_git_patch">Parche de Git</string>
|
||||
<string name="kind_git_repo">Repositorio de Git</string>
|
||||
<string name="kind_git_reply">Respuesta de Git</string>
|
||||
<string name="kind_git_pr">Solicitud de incorporación</string>
|
||||
<string name="kind_git_pr_update">Actualización de solicitud de incorporación</string>
|
||||
<string name="kind_zap_goals">Objetivos de Zap</string>
|
||||
<string name="kind_hashtag_follows">Seguidos por hashtag</string>
|
||||
<string name="kind_highlights">Destacados</string>
|
||||
@@ -2527,20 +2578,27 @@
|
||||
<string name="kind_named_bookmarks">Marcadores con nombre</string>
|
||||
<string name="kind_live_chats">Chats en directo</string>
|
||||
<string name="kind_live_streams">Transmisiones en directo</string>
|
||||
<string name="kind_zaps">Zaps</string>
|
||||
<string name="kind_nwc_request">Solicitud NWC</string>
|
||||
<string name="kind_nwc_response">Respuesta NWC</string>
|
||||
<string name="kind_private_zaps">Zaps privados</string>
|
||||
<string name="kind_zap_req">Solicitud de Zap</string>
|
||||
<string name="kind_blogs">Blogs</string>
|
||||
<string name="kind_meeting_room">Sala de reuniones</string>
|
||||
<string name="kind_room_presence">Presencia en sala</string>
|
||||
<string name="kind_meeting_space">Espacio de reuniones</string>
|
||||
<string name="kind_profile">Perfil</string>
|
||||
<string name="kind_mute_list">Lista de silenciados</string>
|
||||
<string name="kind_nns">NNS</string>
|
||||
<string name="kind_nip">NIP</string>
|
||||
<string name="kind_nostr_connect">Nostr Connect</string>
|
||||
<string name="kind_dvm_status">Estado DVM</string>
|
||||
<string name="kind_dvm_content_req">Solicitud de contenido DVM</string>
|
||||
<string name="kind_dvm_content_resp">Respuesta de contenido DVM</string>
|
||||
<string name="kind_dvm_user_req">Solicitud de usuario DVM</string>
|
||||
<string name="kind_dvm_user_resp">Respuesta de usuario DVM</string>
|
||||
<string name="kind_ots">OTS</string>
|
||||
<string name="kind_pay_to">Pagar a</string>
|
||||
<string name="kind_people_lists">Listas de personas</string>
|
||||
<string name="kind_pictures">Imágenes</string>
|
||||
<string name="kind_workouts">Entrenamientos</string>
|
||||
@@ -2548,6 +2606,7 @@
|
||||
<string name="kind_zap_poll">Encuesta de Zap</string>
|
||||
<string name="kind_poll">Encuesta</string>
|
||||
<string name="kind_poll_response">Respuesta de encuesta</string>
|
||||
<string name="kind_nip04_dms">Mensajes directos NIP-04</string>
|
||||
<string name="kind_private_relays">Relés privados</string>
|
||||
<string name="kind_proxy_relays">Relés proxy</string>
|
||||
<string name="kind_public_message">Mensaje público</string>
|
||||
@@ -2565,6 +2624,7 @@
|
||||
<string name="kind_user_status">Estado del usuario</string>
|
||||
<string name="kind_notes">Notas</string>
|
||||
<string name="kind_edits">Ediciones</string>
|
||||
<string name="kind_torrents">Torrents</string>
|
||||
<string name="kind_torrent_comments">Comentarios de torrent</string>
|
||||
<string name="kind_trusted_relays">Relés de confianza</string>
|
||||
<string name="kind_trusted_providers">Proveedores de confianza</string>
|
||||
|
||||
@@ -712,6 +712,7 @@
|
||||
<item quantity="other">Este nApplet quiere pagar una factura Lightning de %1$d sats.</item>
|
||||
</plurals>
|
||||
<string name="nip82_repository_label">Fuente: %1$s</string>
|
||||
<string name="nip82_version_label">v%1$s</string>
|
||||
<string name="nip82_download">Descargar</string>
|
||||
<string name="nip82_section_about">Acerca de</string>
|
||||
<string name="nip82_section_platforms">Plataformas</string>
|
||||
@@ -870,6 +871,7 @@
|
||||
<string name="route_music_tracks">Música</string>
|
||||
<string name="route_music_playlists">Listas de reproducción</string>
|
||||
<string name="route_podcast_episodes">Episodios</string>
|
||||
<string name="route_podcasts">Podcasts</string>
|
||||
<string name="podcast_view_episodes">Ver episodios</string>
|
||||
<string name="podcast_no_episodes">Aún no se han encontrado episodios</string>
|
||||
<plurals name="podcast_episode_count">
|
||||
@@ -907,6 +909,7 @@
|
||||
<string name="add_hashtag_label">Añadir hashtag</string>
|
||||
<string name="add_hashtag_label_title">Añadir un hashtag</string>
|
||||
<string name="add_hashtag_label_explainer">Etiqueta públicamente esta publicación con un hashtag (etiqueta NIP-32). Las personas que te siguen lo verán en el feed de ese hashtag.</string>
|
||||
<string name="add_hashtag_label_field">Hashtag</string>
|
||||
<string name="add_hashtag_label_confirm">Añadir</string>
|
||||
<string name="hashtag_label_added_by">añadido por</string>
|
||||
<string name="pinned_notes">Notas fijadas</string>
|
||||
@@ -933,6 +936,7 @@
|
||||
<string name="interest_set_name_placeholder">Mis intereses</string>
|
||||
<string name="interest_set_hashtag_add_placeholder">Añadir hashtag</string>
|
||||
<string name="interest_set_hashtag_private_toggle">Privado</string>
|
||||
<string name="interest_set_hashtag_count">%1$d hashtag(s)</string>
|
||||
<string name="interest_set_actions_dialog_title">Acciones del conjunto de intereses</string>
|
||||
<string name="interest_set_rename">Renombrar</string>
|
||||
<string name="interest_set_clone">Clonar</string>
|
||||
@@ -972,6 +976,7 @@
|
||||
<string name="quick_zap_amounts_explainer">Aparece al presionar el botón de zap. Toca una cantidad para eliminarla. Si la dejas vacía, se abrirá el cuadro de diálogo para introducir una cantidad cada vez.</string>
|
||||
<string name="send_onchain_instead">Enviar en cadena en su lugar</string>
|
||||
<string name="reload_mint_title">Recargar mint</string>
|
||||
<string name="reload_mint_sats_amount">%1$s sats</string>
|
||||
<string name="reload_mint_topup_label">Importe de recarga</string>
|
||||
<string name="reload_mint_section_to">Mint a recargar</string>
|
||||
<string name="reload_mint_section_from">Fondos de</string>
|
||||
@@ -1289,6 +1294,7 @@
|
||||
<string name="hide_community_rules_violations_explainer">Elimina publicaciones de los feeds de comunidades cuando la comunidad publica un documento de normas NIP-9B y un evento no lo cumple. No tiene efecto cuando una comunidad no tiene normas estructuradas.</string>
|
||||
<string name="security_section_filtering_preferences">Preferencias de filtrado</string>
|
||||
<string name="security_section_blocked_content">Contenido bloqueado</string>
|
||||
<string name="security_unlimited">∞</string>
|
||||
<string name="security_blocked_users_empty">Aún no has bloqueado a ningún usuario.</string>
|
||||
<string name="security_spamming_users_empty">Ninguna cuenta ha sido marcada como spam en esta sesión.</string>
|
||||
<string name="security_hidden_words_empty">No hay palabras ocultas. Añade una palabra a continuación para ocultar las publicaciones que la contengan.</string>
|
||||
@@ -1347,6 +1353,7 @@
|
||||
<string name="relay_count_subscriptions">Suscripciones COUNT (%1$d)</string>
|
||||
<string name="relay_outbox_events">Eventos de salida (%1$d)</string>
|
||||
<string name="relay_filter_authors">%1$d autores</string>
|
||||
<string name="relay_filter_ids">%1$d ids</string>
|
||||
<string name="relay_filter_since">desde %1$s</string>
|
||||
<string name="relay_filter_until">hasta %1$s</string>
|
||||
<string name="relay_filter_limit">límite %1$d</string>
|
||||
@@ -1357,6 +1364,7 @@
|
||||
<string name="accepts_up_to">Acepta hasta</string>
|
||||
<string name="time_in_the_future">%1$s en el futuro</string>
|
||||
<string name="time_in_the_past">hace %1$s</string>
|
||||
<string name="amount_in_bits">%1$s bits</string>
|
||||
<string name="event_retention">Retención de eventos</string>
|
||||
<string name="content_size">Tamaño de contenido</string>
|
||||
<string name="connectivity">Conectividad</string>
|
||||
@@ -1370,6 +1378,7 @@
|
||||
<string name="max_limit_events_returning">Límite máximo (devolución de eventos)</string>
|
||||
<string name="max_subid_length">Longitud máxima de subID</string>
|
||||
<string name="cashu">Token de Cashu</string>
|
||||
<string name="cashu_mint_label">Mint: %1$s</string>
|
||||
<string name="cashu_redeem">Canjear</string>
|
||||
<string name="cashu_copy_token">Copiar token</string>
|
||||
<string name="quick_actions_open_in_another_app">Abrir en otra app</string>
|
||||
@@ -1727,6 +1736,7 @@
|
||||
<string name="wallet_outgoing">Enviado</string>
|
||||
<string name="wallet_refresh">Actualizar</string>
|
||||
<string name="wallet_filter_all">Todos</string>
|
||||
<string name="wallet_filter_zaps">Zaps</string>
|
||||
<string name="wallet_filter_non_zaps">No-Zaps</string>
|
||||
<string name="wallet_add">Añadir cartera</string>
|
||||
<string name="wallet_default">Predeterminada</string>
|
||||
@@ -1750,10 +1760,13 @@
|
||||
<string name="cashu_discovering">Buscando tu monedero…</string>
|
||||
<string name="cashu_discovering_description">Los monederos NIP-60 se sincronizan entre clientes. Si creaste uno en otra aplicación con esta clave Nostr, debería aparecer en unos segundos.</string>
|
||||
<string name="cashu_balance">Saldo</string>
|
||||
<string name="cashu_mints">Mints</string>
|
||||
<string name="cashu_mint_url">URL del mint</string>
|
||||
<string name="cashu_remove_mint">Eliminar mint</string>
|
||||
<string name="cashu_add_mint">Añadir mint</string>
|
||||
<string name="cashu_history">Historial</string>
|
||||
<string name="cashu_wallet_autosaves">Tu billetera guarda automáticamente a medida que añades o eliminas mints. Se crea una clave nutzap la primera vez que añades una mint.</string>
|
||||
<string name="cashu_wallet_saving">Guardando…</string>
|
||||
<string name="cashu_p2pk_section">Clave nutzap (avanzado)</string>
|
||||
<string name="cashu_p2pk_explainer">Una clave privada separada usada únicamente para recibir nutzaps NIP-61. No es tu clave de identidad Nostr.</string>
|
||||
<string name="cashu_p2pk_autogen">Generar una nueva clave</string>
|
||||
@@ -1826,7 +1839,9 @@
|
||||
<string name="cashu_quote_confirm">¿Pagar %1$s sat + hasta %2$s sat en comisiones?</string>
|
||||
<string name="cashu_verify">Verificar</string>
|
||||
<string name="cashu_mint_reachable">✓ El mint es accesible</string>
|
||||
<string name="cashu_mint_reachable_named">✓ %1$s</string>
|
||||
<string name="cashu_mint_unreachable">No se pudo alcanzar el mint: %1$s</string>
|
||||
<string name="nutzap">Nutzap</string>
|
||||
<string name="nutzap_failed_title">Nutzap fallido</string>
|
||||
<string name="nutzap_failed_no_recipient">No hay clave pública de destinatario en la nota</string>
|
||||
<string name="nutzap_failed_private_note">Los nutzaps son públicos y revelarían esta nota privada. Usa un zap Lightning en su lugar.</string>
|
||||
@@ -1868,12 +1883,18 @@
|
||||
<string name="onchain_send_recipient_placeholder">nombre, NIP-05 o npub</string>
|
||||
<string name="onchain_send_change_recipient">Cambiar destinatario</string>
|
||||
<string name="onchain_send_amount">Cantidad</string>
|
||||
<string name="onchain_send_sats_suffix">sats</string>
|
||||
<string name="onchain_send_min_warning">El zap onchain mínimo es %1$s sats — las cantidades menores son consumidas por las comisiones de minería. Usa un zap Lightning en su lugar.</string>
|
||||
<string name="onchain_send_comment_label">Comentario (opcional)</string>
|
||||
<string name="onchain_send_priority">Prioridad</string>
|
||||
<string name="onchain_send_fee_slow">Lento</string>
|
||||
<string name="onchain_send_fee_normal">Normal</string>
|
||||
<string name="onchain_send_fee_fast">Rápido</string>
|
||||
<string name="onchain_send_fee_eta_slow">~1 h</string>
|
||||
<string name="onchain_send_fee_eta_normal">~30 min</string>
|
||||
<string name="onchain_send_fee_eta_fast">~10 min</string>
|
||||
<string name="onchain_send_fee_rate_eta">%1$s sat/vB · %2$s</string>
|
||||
<string name="onchain_send_fee_tier_label_rate_eta">%1$s · %2$s sat/vB · %3$s</string>
|
||||
<string name="onchain_send_loading_fees">Cargando estimaciones de comisión…</string>
|
||||
<string name="onchain_send_button_amount">Enviar %1$s sats</string>
|
||||
<plurals name="onchain_send_button_amount_split">
|
||||
@@ -1898,6 +1919,7 @@
|
||||
<string name="onchain_send_result_transaction">Transacción</string>
|
||||
<string name="onchain_send_result_fee">Comisión</string>
|
||||
<string name="onchain_send_result_change">Cambio</string>
|
||||
<string name="onchain_send_sats_amount">%1$s sats</string>
|
||||
<string name="onchain_send_done">Hecho</string>
|
||||
<string name="onchain_send_failed_at">Error en: %1$s</string>
|
||||
<string name="onchain_send_broadcast_no_receipt">El pago se difundió (tx %1$s) pero el recibo no se publicó.</string>
|
||||
@@ -1932,6 +1954,7 @@
|
||||
<string name="edit_calendar_collection">Editar calendario</string>
|
||||
<string name="calendar_collection_events_section">Eventos en este calendario (%1$d)</string>
|
||||
<string name="calendar_collection_no_events_yet">Aún no has creado ningún evento de calendario.</string>
|
||||
<string name="calendar_view_feed">Tablón</string>
|
||||
<string name="calendar_view_month">Mes</string>
|
||||
<string name="calendar_view_week">Semana</string>
|
||||
<string name="calendar_view_day">Día</string>
|
||||
@@ -1993,6 +2016,7 @@
|
||||
<string name="calendar_rsvp_going">Asistiré</string>
|
||||
<string name="calendar_rsvp_maybe">Tal vez</string>
|
||||
<string name="calendar_rsvp_not_going">No puedo ir</string>
|
||||
<string name="calendar_rsvp_section">Confirmaciones (%1$d)</string>
|
||||
<string name="calendar_rsvp_none">Sin RSVPs aún.</string>
|
||||
<string name="calendar_participants_section">Participantes (%1$d)</string>
|
||||
<string name="calendar_event_in_calendars">En calendarios (%1$d)</string>
|
||||
@@ -2002,6 +2026,7 @@
|
||||
<string name="calendar_relative_ongoing_with_end">%1$s · termina %2$s</string>
|
||||
<string name="calendar_export_share_title">Compartir evento de calendario</string>
|
||||
<string name="calendar_export_event">Exportar al calendario (.ics)</string>
|
||||
<string name="calendar_reminder_channel_id">recordatorios_calendario</string>
|
||||
<string name="calendar_reminder_channel_name">Recordatorios de calendario</string>
|
||||
<string name="calendar_reminder_channel_description">Aviso cuando un evento al que asistirás está a punto de comenzar.</string>
|
||||
<string name="calendar_reminder_default_title">Evento de calendario</string>
|
||||
@@ -2026,6 +2051,10 @@
|
||||
<string name="calendar_reminder_settings_enabled_subtitle">Se enviará una notificación cuando un evento al que asistirás esté a punto de comenzar.</string>
|
||||
<string name="calendar_reminder_settings_lead_title">Tiempo de antelación del recordatorio</string>
|
||||
<string name="calendar_reminder_settings_lead_subtitle">Cuántos minutos antes del evento quieres recibir la notificación.</string>
|
||||
<plurals name="calendar_reminder_settings_lead_choice">
|
||||
<item quantity="one">%1$d min</item>
|
||||
<item quantity="other">%1$d min</item>
|
||||
</plurals>
|
||||
<string name="calendar_share_nostr">Compartir como enlace Nostr</string>
|
||||
<string name="calendar_share_nostr_title">Compartir enlace de calendario</string>
|
||||
<string name="calendar_filter_all">Todos los calendarios</string>
|
||||
@@ -2174,6 +2203,7 @@
|
||||
<string name="private_inbox_section">Relés de entrada DM</string>
|
||||
<string name="private_inbox_section_explainer_profile">El usuario recibe mensajes directos en estos relés</string>
|
||||
<string name="private_inbox_section_explainer">Inserta entre 1 y 3 relés para que te sirvan de buzón de entrada privado. Otros usarán estos relés para enviarte mensajes directos. Los relés del buzón de entrada para mensajes directos deberían aceptar cualquier mensaje de cualquier persona, pero solo te permiten descargarlos. Algunas buenas opciones son:\n - inbox.nostr.wine (de pago)\n - you.nostr1.com (relés personales; de pago)</string>
|
||||
<string name="keypackages">Paquetes de claves</string>
|
||||
<string name="keypackage_section">Relays de KeyPackage</string>
|
||||
<string name="keypackage_section_explainer">Relays donde se publican tus MLS KeyPackages (MIP-00). Otros usuarios obtienen estas KeyPackages para invitarte a chats de grupo Marmot. Introduce entre 1 y 3 relays que acepten eventos KeyPackage de tu parte y permitan lecturas públicas.</string>
|
||||
<string name="private_outbox_section">Relés privados</string>
|
||||
@@ -2218,17 +2248,27 @@
|
||||
<string name="forked_from">Bifurcación de</string>
|
||||
<string name="git_web_address">Web:</string>
|
||||
<string name="git_clone_address">Clon:</string>
|
||||
<string name="git_branch">Rama</string>
|
||||
<string name="git_commit">Confirmar</string>
|
||||
<string name="git_merge_base">Fusionar base</string>
|
||||
<string name="git_pr_update_description">Se actualizó la solicitud de incorporación de cambios con una nueva confirmación.</string>
|
||||
<string name="git_status_open">Abierto</string>
|
||||
<string name="git_status_merged">Fusionado</string>
|
||||
<string name="git_status_closed">Cerrado</string>
|
||||
<string name="git_status_draft">Borrador</string>
|
||||
<string name="git_repo_tab_overview">Resumen</string>
|
||||
<string name="git_repo_tab_issues">Incidencias</string>
|
||||
<string name="git_repo_tab_patches">Parches y PRs</string>
|
||||
<string name="git_repo_filter_open">Abierto</string>
|
||||
<string name="git_repo_filter_closed">Cerrado y resuelto</string>
|
||||
<string name="git_untitled">Sin título</string>
|
||||
<string name="git_repo_section_about">Acerca de</string>
|
||||
<string name="git_repo_section_links">Enlaces</string>
|
||||
<string name="git_repo_section_maintainers">Mantenedores</string>
|
||||
<string name="git_repo_section_topics">Temas</string>
|
||||
<string name="git_repo_personal_fork">Fork personal</string>
|
||||
<string name="nsite_title">nSite: %1$s</string>
|
||||
<string name="napplet_card_title">nApplet: %1$s</string>
|
||||
<string name="napplet_card_permissions">Permisos:</string>
|
||||
<string name="profile_tab_apps">Apps y sitios</string>
|
||||
<string name="nsite_root_site">Sitio raíz</string>
|
||||
@@ -2302,12 +2342,15 @@
|
||||
<string name="torrent_no_info">El evento no tiene suficiente información para crear un enlace magnético</string>
|
||||
<string name="my_lists">Mis listas</string>
|
||||
<string name="select_list_to_filter">Selecciona una lista para filtrar el feed</string>
|
||||
<string name="feed_group_feeds">Tablones</string>
|
||||
<string name="feed_group_hashtags">Hashtags</string>
|
||||
<string name="feed_group_interest_sets">Conjuntos de intereses</string>
|
||||
<string name="feed_group_locations">Ubicaciones</string>
|
||||
<string name="feed_group_communities">Comunidades</string>
|
||||
<string name="feed_group_lists">Listas</string>
|
||||
<string name="feed_group_dvms">Algoritmos de feed</string>
|
||||
<string name="follow_list_all_favorite_dvms">Todos los algoritmos de feed favoritos</string>
|
||||
<string name="feed_group_relays">Relés</string>
|
||||
<string name="add_dvm_to_favorites">Añadir algoritmo de feed a favoritos</string>
|
||||
<string name="remove_dvm_from_favorites">Eliminar de favoritos</string>
|
||||
<string name="favorite_dvms_title">Algoritmos de feed favoritos</string>
|
||||
@@ -2337,10 +2380,13 @@
|
||||
<string name="search_scope_all">Todo</string>
|
||||
<string name="search_scope_people">Personas</string>
|
||||
<string name="search_scope_notes">Notas</string>
|
||||
<string name="search_source_local">Local</string>
|
||||
<string name="search_source_relays">Relés</string>
|
||||
<string name="search_follows_only">Solo seguidos</string>
|
||||
<string name="search_sort_newest">Más recientes</string>
|
||||
<string name="search_sort_oldest">Más antiguos</string>
|
||||
<string name="search_sort_relevance">Relevancia</string>
|
||||
<string name="search_sort_popular">Popular</string>
|
||||
<string name="search_filters_title">Filtros</string>
|
||||
<string name="search_filters_reset">Restablecer</string>
|
||||
<string name="search_filters_section_source">Fuente</string>
|
||||
@@ -2352,6 +2398,7 @@
|
||||
<string name="translate_to_description">Elige el idioma al que traducir el contenido.</string>
|
||||
<string name="language_preferences">Preferencias de visualización de idioma</string>
|
||||
<string name="language_preferences_description">Para cada par de idiomas traducidos, elige qué idioma mostrar primero.</string>
|
||||
<string name="language_preference_pair">%1$s → %2$s</string>
|
||||
<string name="search_languages">Buscar idiomas</string>
|
||||
<string name="add_language">Añadir idioma</string>
|
||||
<string name="add_language_pair">Añadir par de idiomas</string>
|
||||
@@ -2408,6 +2455,7 @@
|
||||
<string name="retrying">Reintentando…</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="dismiss">Descartar</string>
|
||||
<string name="share_of">%1$d/%2$d</string>
|
||||
<string name="broadcasting">Difundiendo</string>
|
||||
<string name="broadcasting_name">Difundiendo %1$s</string>
|
||||
<string name="broadcasting_number_events">Difundiendo %1$d eventos…</string>
|
||||
@@ -2504,10 +2552,13 @@
|
||||
<string name="kind_follow_packs">Packs de seguidos</string>
|
||||
<string name="kind_reposts_16">Republicaciones (16)</string>
|
||||
<string name="kind_geohash_follows">Seguidos por geohash</string>
|
||||
<string name="kind_gift_wraps">GiftWraps</string>
|
||||
<string name="kind_git_issue">Incidencia de Git</string>
|
||||
<string name="kind_git_patch">Parche de Git</string>
|
||||
<string name="kind_git_repo">Repositorio de Git</string>
|
||||
<string name="kind_git_reply">Respuesta de Git</string>
|
||||
<string name="kind_git_pr">Solicitud de incorporación</string>
|
||||
<string name="kind_git_pr_update">Actualización de solicitud de incorporación</string>
|
||||
<string name="kind_zap_goals">Objetivos de Zap</string>
|
||||
<string name="kind_hashtag_follows">Seguidos por hashtag</string>
|
||||
<string name="kind_highlights">Destacados</string>
|
||||
@@ -2519,20 +2570,27 @@
|
||||
<string name="kind_named_bookmarks">Marcadores con nombre</string>
|
||||
<string name="kind_live_chats">Chats en directo</string>
|
||||
<string name="kind_live_streams">Transmisiones en directo</string>
|
||||
<string name="kind_zaps">Zaps</string>
|
||||
<string name="kind_nwc_request">Solicitud NWC</string>
|
||||
<string name="kind_nwc_response">Respuesta NWC</string>
|
||||
<string name="kind_private_zaps">Zaps privados</string>
|
||||
<string name="kind_zap_req">Solicitud de Zap</string>
|
||||
<string name="kind_blogs">Blogs</string>
|
||||
<string name="kind_meeting_room">Sala de reuniones</string>
|
||||
<string name="kind_room_presence">Presencia en sala</string>
|
||||
<string name="kind_meeting_space">Espacio de reuniones</string>
|
||||
<string name="kind_profile">Perfil</string>
|
||||
<string name="kind_mute_list">Lista de silenciados</string>
|
||||
<string name="kind_nns">NNS</string>
|
||||
<string name="kind_nip">NIP</string>
|
||||
<string name="kind_nostr_connect">Nostr Connect</string>
|
||||
<string name="kind_dvm_status">Estado DVM</string>
|
||||
<string name="kind_dvm_content_req">Solicitud de contenido DVM</string>
|
||||
<string name="kind_dvm_content_resp">Respuesta de contenido DVM</string>
|
||||
<string name="kind_dvm_user_req">Solicitud de usuario DVM</string>
|
||||
<string name="kind_dvm_user_resp">Respuesta de usuario DVM</string>
|
||||
<string name="kind_ots">OTS</string>
|
||||
<string name="kind_pay_to">Pagar a</string>
|
||||
<string name="kind_people_lists">Listas de personas</string>
|
||||
<string name="kind_pictures">Imágenes</string>
|
||||
<string name="kind_workouts">Entrenamientos</string>
|
||||
@@ -2540,6 +2598,7 @@
|
||||
<string name="kind_zap_poll">Encuesta de Zap</string>
|
||||
<string name="kind_poll">Encuesta</string>
|
||||
<string name="kind_poll_response">Respuesta de encuesta</string>
|
||||
<string name="kind_nip04_dms">Mensajes directos NIP-04</string>
|
||||
<string name="kind_private_relays">Relés privados</string>
|
||||
<string name="kind_proxy_relays">Relés proxy</string>
|
||||
<string name="kind_public_message">Mensaje público</string>
|
||||
@@ -2557,6 +2616,7 @@
|
||||
<string name="kind_user_status">Estado del usuario</string>
|
||||
<string name="kind_notes">Notas</string>
|
||||
<string name="kind_edits">Ediciones</string>
|
||||
<string name="kind_torrents">Torrents</string>
|
||||
<string name="kind_torrent_comments">Comentarios de torrent</string>
|
||||
<string name="kind_trusted_relays">Relés de confianza</string>
|
||||
<string name="kind_trusted_providers">Proveedores de confianza</string>
|
||||
|
||||
@@ -712,6 +712,7 @@
|
||||
<item quantity="other">Este nApplet quiere pagar una factura Lightning de %1$d sats.</item>
|
||||
</plurals>
|
||||
<string name="nip82_repository_label">Fuente: %1$s</string>
|
||||
<string name="nip82_version_label">v%1$s</string>
|
||||
<string name="nip82_download">Descargar</string>
|
||||
<string name="nip82_section_about">Acerca de</string>
|
||||
<string name="nip82_section_platforms">Plataformas</string>
|
||||
@@ -870,6 +871,7 @@
|
||||
<string name="route_music_tracks">Música</string>
|
||||
<string name="route_music_playlists">Listas de reproducción</string>
|
||||
<string name="route_podcast_episodes">Episodios</string>
|
||||
<string name="route_podcasts">Podcasts</string>
|
||||
<string name="podcast_view_episodes">Ver episodios</string>
|
||||
<string name="podcast_no_episodes">Aún no se han encontrado episodios</string>
|
||||
<plurals name="podcast_episode_count">
|
||||
@@ -907,6 +909,7 @@
|
||||
<string name="add_hashtag_label">Añadir hashtag</string>
|
||||
<string name="add_hashtag_label_title">Añadir un hashtag</string>
|
||||
<string name="add_hashtag_label_explainer">Etiqueta públicamente esta publicación con un hashtag (etiqueta NIP-32). Las personas que te siguen lo verán en el feed de ese hashtag.</string>
|
||||
<string name="add_hashtag_label_field">Hashtag</string>
|
||||
<string name="add_hashtag_label_confirm">Añadir</string>
|
||||
<string name="hashtag_label_added_by">añadido por</string>
|
||||
<string name="pinned_notes">Notas fijadas</string>
|
||||
@@ -933,6 +936,7 @@
|
||||
<string name="interest_set_name_placeholder">Mis intereses</string>
|
||||
<string name="interest_set_hashtag_add_placeholder">Añadir hashtag</string>
|
||||
<string name="interest_set_hashtag_private_toggle">Privado</string>
|
||||
<string name="interest_set_hashtag_count">%1$d hashtag(s)</string>
|
||||
<string name="interest_set_actions_dialog_title">Acciones del conjunto de intereses</string>
|
||||
<string name="interest_set_rename">Renombrar</string>
|
||||
<string name="interest_set_clone">Clonar</string>
|
||||
@@ -972,6 +976,7 @@
|
||||
<string name="quick_zap_amounts_explainer">Aparece al presionar el botón de zap. Toca una cantidad para eliminarla. Si la dejas vacía, se abrirá el cuadro de diálogo para introducir una cantidad cada vez.</string>
|
||||
<string name="send_onchain_instead">Enviar en cadena en su lugar</string>
|
||||
<string name="reload_mint_title">Recargar mint</string>
|
||||
<string name="reload_mint_sats_amount">%1$s sats</string>
|
||||
<string name="reload_mint_topup_label">Importe de recarga</string>
|
||||
<string name="reload_mint_section_to">Mint a recargar</string>
|
||||
<string name="reload_mint_section_from">Fondos de</string>
|
||||
@@ -1289,6 +1294,7 @@
|
||||
<string name="hide_community_rules_violations_explainer">Elimina publicaciones de los feeds de comunidades cuando la comunidad publica un documento de normas NIP-9B y un evento no lo cumple. No tiene efecto cuando una comunidad no tiene normas estructuradas.</string>
|
||||
<string name="security_section_filtering_preferences">Preferencias de filtrado</string>
|
||||
<string name="security_section_blocked_content">Contenido bloqueado</string>
|
||||
<string name="security_unlimited">∞</string>
|
||||
<string name="security_blocked_users_empty">Aún no has bloqueado a ningún usuario.</string>
|
||||
<string name="security_spamming_users_empty">Ninguna cuenta ha sido marcada como spam en esta sesión.</string>
|
||||
<string name="security_hidden_words_empty">No hay palabras ocultas. Añade una palabra a continuación para ocultar las publicaciones que la contengan.</string>
|
||||
@@ -1347,6 +1353,7 @@
|
||||
<string name="relay_count_subscriptions">Suscripciones COUNT (%1$d)</string>
|
||||
<string name="relay_outbox_events">Eventos de salida (%1$d)</string>
|
||||
<string name="relay_filter_authors">%1$d autores</string>
|
||||
<string name="relay_filter_ids">%1$d ids</string>
|
||||
<string name="relay_filter_since">desde %1$s</string>
|
||||
<string name="relay_filter_until">hasta %1$s</string>
|
||||
<string name="relay_filter_limit">límite %1$d</string>
|
||||
@@ -1357,6 +1364,7 @@
|
||||
<string name="accepts_up_to">Acepta hasta</string>
|
||||
<string name="time_in_the_future">%1$s en el futuro</string>
|
||||
<string name="time_in_the_past">hace %1$s</string>
|
||||
<string name="amount_in_bits">%1$s bits</string>
|
||||
<string name="event_retention">Retención de eventos</string>
|
||||
<string name="content_size">Tamaño de contenido</string>
|
||||
<string name="connectivity">Conectividad</string>
|
||||
@@ -1370,6 +1378,7 @@
|
||||
<string name="max_limit_events_returning">Límite máximo (devolución de eventos)</string>
|
||||
<string name="max_subid_length">Longitud máxima de subID</string>
|
||||
<string name="cashu">Token de Cashu</string>
|
||||
<string name="cashu_mint_label">Mint: %1$s</string>
|
||||
<string name="cashu_redeem">Canjear</string>
|
||||
<string name="cashu_copy_token">Copiar token</string>
|
||||
<string name="quick_actions_open_in_another_app">Abrir en otra app</string>
|
||||
@@ -1727,6 +1736,7 @@
|
||||
<string name="wallet_outgoing">Enviado</string>
|
||||
<string name="wallet_refresh">Actualizar</string>
|
||||
<string name="wallet_filter_all">Todos</string>
|
||||
<string name="wallet_filter_zaps">Zaps</string>
|
||||
<string name="wallet_filter_non_zaps">No-Zaps</string>
|
||||
<string name="wallet_add">Añadir cartera</string>
|
||||
<string name="wallet_default">Predeterminada</string>
|
||||
@@ -1750,10 +1760,13 @@
|
||||
<string name="cashu_discovering">Buscando tu monedero…</string>
|
||||
<string name="cashu_discovering_description">Los monederos NIP-60 se sincronizan entre clientes. Si creaste uno en otra aplicación con esta clave Nostr, debería aparecer en unos segundos.</string>
|
||||
<string name="cashu_balance">Saldo</string>
|
||||
<string name="cashu_mints">Mints</string>
|
||||
<string name="cashu_mint_url">URL del mint</string>
|
||||
<string name="cashu_remove_mint">Eliminar mint</string>
|
||||
<string name="cashu_add_mint">Añadir mint</string>
|
||||
<string name="cashu_history">Historial</string>
|
||||
<string name="cashu_wallet_autosaves">Tu billetera guarda automáticamente a medida que añades o eliminas mints. Se crea una clave nutzap la primera vez que añades una mint.</string>
|
||||
<string name="cashu_wallet_saving">Guardando…</string>
|
||||
<string name="cashu_p2pk_section">Clave nutzap (avanzado)</string>
|
||||
<string name="cashu_p2pk_explainer">Una clave privada separada usada únicamente para recibir nutzaps NIP-61. No es tu clave de identidad Nostr.</string>
|
||||
<string name="cashu_p2pk_autogen">Generar una nueva clave</string>
|
||||
@@ -1826,7 +1839,9 @@
|
||||
<string name="cashu_quote_confirm">¿Pagar %1$s sat + hasta %2$s sat en comisiones?</string>
|
||||
<string name="cashu_verify">Verificar</string>
|
||||
<string name="cashu_mint_reachable">✓ El mint es accesible</string>
|
||||
<string name="cashu_mint_reachable_named">✓ %1$s</string>
|
||||
<string name="cashu_mint_unreachable">No se pudo alcanzar el mint: %1$s</string>
|
||||
<string name="nutzap">Nutzap</string>
|
||||
<string name="nutzap_failed_title">Nutzap fallido</string>
|
||||
<string name="nutzap_failed_no_recipient">No hay clave pública de destinatario en la nota</string>
|
||||
<string name="nutzap_failed_private_note">Los nutzaps son públicos y revelarían esta nota privada. Usa un zap Lightning en su lugar.</string>
|
||||
@@ -1868,12 +1883,18 @@
|
||||
<string name="onchain_send_recipient_placeholder">nombre, NIP-05 o npub</string>
|
||||
<string name="onchain_send_change_recipient">Cambiar destinatario</string>
|
||||
<string name="onchain_send_amount">Cantidad</string>
|
||||
<string name="onchain_send_sats_suffix">sats</string>
|
||||
<string name="onchain_send_min_warning">El zap onchain mínimo es %1$s sats — las cantidades menores son consumidas por las comisiones de minería. Usa un zap Lightning en su lugar.</string>
|
||||
<string name="onchain_send_comment_label">Comentario (opcional)</string>
|
||||
<string name="onchain_send_priority">Prioridad</string>
|
||||
<string name="onchain_send_fee_slow">Lento</string>
|
||||
<string name="onchain_send_fee_normal">Normal</string>
|
||||
<string name="onchain_send_fee_fast">Rápido</string>
|
||||
<string name="onchain_send_fee_eta_slow">~1 h</string>
|
||||
<string name="onchain_send_fee_eta_normal">~30 min</string>
|
||||
<string name="onchain_send_fee_eta_fast">~10 min</string>
|
||||
<string name="onchain_send_fee_rate_eta">%1$s sat/vB · %2$s</string>
|
||||
<string name="onchain_send_fee_tier_label_rate_eta">%1$s · %2$s sat/vB · %3$s</string>
|
||||
<string name="onchain_send_loading_fees">Cargando estimaciones de comisión…</string>
|
||||
<string name="onchain_send_button_amount">Enviar %1$s sats</string>
|
||||
<plurals name="onchain_send_button_amount_split">
|
||||
@@ -1898,6 +1919,7 @@
|
||||
<string name="onchain_send_result_transaction">Transacción</string>
|
||||
<string name="onchain_send_result_fee">Comisión</string>
|
||||
<string name="onchain_send_result_change">Cambio</string>
|
||||
<string name="onchain_send_sats_amount">%1$s sats</string>
|
||||
<string name="onchain_send_done">Hecho</string>
|
||||
<string name="onchain_send_failed_at">Error en: %1$s</string>
|
||||
<string name="onchain_send_broadcast_no_receipt">El pago se difundió (tx %1$s) pero el recibo no se publicó.</string>
|
||||
@@ -1932,6 +1954,7 @@
|
||||
<string name="edit_calendar_collection">Editar calendario</string>
|
||||
<string name="calendar_collection_events_section">Eventos en este calendario (%1$d)</string>
|
||||
<string name="calendar_collection_no_events_yet">Aún no has creado ningún evento de calendario.</string>
|
||||
<string name="calendar_view_feed">Tablón</string>
|
||||
<string name="calendar_view_month">Mes</string>
|
||||
<string name="calendar_view_week">Semana</string>
|
||||
<string name="calendar_view_day">Día</string>
|
||||
@@ -1993,6 +2016,7 @@
|
||||
<string name="calendar_rsvp_going">Asistiré</string>
|
||||
<string name="calendar_rsvp_maybe">Tal vez</string>
|
||||
<string name="calendar_rsvp_not_going">No puedo ir</string>
|
||||
<string name="calendar_rsvp_section">Confirmaciones (%1$d)</string>
|
||||
<string name="calendar_rsvp_none">Sin RSVPs aún.</string>
|
||||
<string name="calendar_participants_section">Participantes (%1$d)</string>
|
||||
<string name="calendar_event_in_calendars">En calendarios (%1$d)</string>
|
||||
@@ -2002,6 +2026,7 @@
|
||||
<string name="calendar_relative_ongoing_with_end">%1$s · termina %2$s</string>
|
||||
<string name="calendar_export_share_title">Compartir evento de calendario</string>
|
||||
<string name="calendar_export_event">Exportar al calendario (.ics)</string>
|
||||
<string name="calendar_reminder_channel_id">recordatorios_calendario</string>
|
||||
<string name="calendar_reminder_channel_name">Recordatorios de calendario</string>
|
||||
<string name="calendar_reminder_channel_description">Aviso cuando un evento al que asistirás está a punto de comenzar.</string>
|
||||
<string name="calendar_reminder_default_title">Evento de calendario</string>
|
||||
@@ -2026,6 +2051,10 @@
|
||||
<string name="calendar_reminder_settings_enabled_subtitle">Se enviará una notificación cuando un evento al que asistirás esté a punto de comenzar.</string>
|
||||
<string name="calendar_reminder_settings_lead_title">Tiempo de antelación del recordatorio</string>
|
||||
<string name="calendar_reminder_settings_lead_subtitle">Cuántos minutos antes del evento quieres recibir la notificación.</string>
|
||||
<plurals name="calendar_reminder_settings_lead_choice">
|
||||
<item quantity="one">%1$d min</item>
|
||||
<item quantity="other">%1$d min</item>
|
||||
</plurals>
|
||||
<string name="calendar_share_nostr">Compartir como enlace Nostr</string>
|
||||
<string name="calendar_share_nostr_title">Compartir enlace de calendario</string>
|
||||
<string name="calendar_filter_all">Todos los calendarios</string>
|
||||
@@ -2174,6 +2203,7 @@
|
||||
<string name="private_inbox_section">Relés de entrada DM</string>
|
||||
<string name="private_inbox_section_explainer_profile">El usuario recibe mensajes directos en estos relés</string>
|
||||
<string name="private_inbox_section_explainer">Inserta entre 1 y 3 relés para que te sirvan de buzón de entrada privado. Otros usarán estos relés para enviarte mensajes directos. Los relés del buzón de entrada para mensajes directos deberían aceptar cualquier mensaje de cualquier persona, pero solo te permiten descargarlos. Algunas buenas opciones son:\n - inbox.nostr.wine (de pago)\n - you.nostr1.com (relés personales; de pago)</string>
|
||||
<string name="keypackages">Paquetes de claves</string>
|
||||
<string name="keypackage_section">Relays de KeyPackage</string>
|
||||
<string name="keypackage_section_explainer">Relays donde se publican tus MLS KeyPackages (MIP-00). Otros usuarios obtienen estas KeyPackages para invitarte a chats de grupo Marmot. Introduce entre 1 y 3 relays que acepten eventos KeyPackage de tu parte y permitan lecturas públicas.</string>
|
||||
<string name="private_outbox_section">Relés privados</string>
|
||||
@@ -2218,17 +2248,27 @@
|
||||
<string name="forked_from">Bifurcación de</string>
|
||||
<string name="git_web_address">Web:</string>
|
||||
<string name="git_clone_address">Clon:</string>
|
||||
<string name="git_branch">Rama</string>
|
||||
<string name="git_commit">Confirmar</string>
|
||||
<string name="git_merge_base">Fusionar base</string>
|
||||
<string name="git_pr_update_description">Se actualizó la solicitud de incorporación de cambios con una nueva confirmación.</string>
|
||||
<string name="git_status_open">Abierto</string>
|
||||
<string name="git_status_merged">Fusionado</string>
|
||||
<string name="git_status_closed">Cerrado</string>
|
||||
<string name="git_status_draft">Borrador</string>
|
||||
<string name="git_repo_tab_overview">Resumen</string>
|
||||
<string name="git_repo_tab_issues">Incidencias</string>
|
||||
<string name="git_repo_tab_patches">Parches y PRs</string>
|
||||
<string name="git_repo_filter_open">Abierto</string>
|
||||
<string name="git_repo_filter_closed">Cerrado y resuelto</string>
|
||||
<string name="git_untitled">Sin título</string>
|
||||
<string name="git_repo_section_about">Acerca de</string>
|
||||
<string name="git_repo_section_links">Enlaces</string>
|
||||
<string name="git_repo_section_maintainers">Mantenedores</string>
|
||||
<string name="git_repo_section_topics">Temas</string>
|
||||
<string name="git_repo_personal_fork">Fork personal</string>
|
||||
<string name="nsite_title">nSite: %1$s</string>
|
||||
<string name="napplet_card_title">nApplet: %1$s</string>
|
||||
<string name="napplet_card_permissions">Permisos:</string>
|
||||
<string name="profile_tab_apps">Apps y sitios</string>
|
||||
<string name="nsite_root_site">Sitio raíz</string>
|
||||
@@ -2302,12 +2342,15 @@
|
||||
<string name="torrent_no_info">El evento no tiene suficiente información para crear un enlace magnético</string>
|
||||
<string name="my_lists">Mis listas</string>
|
||||
<string name="select_list_to_filter">Selecciona una lista para filtrar el feed</string>
|
||||
<string name="feed_group_feeds">Tablones</string>
|
||||
<string name="feed_group_hashtags">Hashtags</string>
|
||||
<string name="feed_group_interest_sets">Conjuntos de intereses</string>
|
||||
<string name="feed_group_locations">Ubicaciones</string>
|
||||
<string name="feed_group_communities">Comunidades</string>
|
||||
<string name="feed_group_lists">Listas</string>
|
||||
<string name="feed_group_dvms">Algoritmos de feed</string>
|
||||
<string name="follow_list_all_favorite_dvms">Todos los algoritmos de feed favoritos</string>
|
||||
<string name="feed_group_relays">Relés</string>
|
||||
<string name="add_dvm_to_favorites">Añadir algoritmo de feed a favoritos</string>
|
||||
<string name="remove_dvm_from_favorites">Eliminar de favoritos</string>
|
||||
<string name="favorite_dvms_title">Algoritmos de feed favoritos</string>
|
||||
@@ -2337,10 +2380,13 @@
|
||||
<string name="search_scope_all">Todo</string>
|
||||
<string name="search_scope_people">Personas</string>
|
||||
<string name="search_scope_notes">Notas</string>
|
||||
<string name="search_source_local">Local</string>
|
||||
<string name="search_source_relays">Relés</string>
|
||||
<string name="search_follows_only">Solo seguidos</string>
|
||||
<string name="search_sort_newest">Más recientes</string>
|
||||
<string name="search_sort_oldest">Más antiguos</string>
|
||||
<string name="search_sort_relevance">Relevancia</string>
|
||||
<string name="search_sort_popular">Popular</string>
|
||||
<string name="search_filters_title">Filtros</string>
|
||||
<string name="search_filters_reset">Restablecer</string>
|
||||
<string name="search_filters_section_source">Fuente</string>
|
||||
@@ -2352,6 +2398,7 @@
|
||||
<string name="translate_to_description">Elige el idioma al que traducir el contenido.</string>
|
||||
<string name="language_preferences">Preferencias de visualización de idioma</string>
|
||||
<string name="language_preferences_description">Para cada par de idiomas traducidos, elige qué idioma mostrar primero.</string>
|
||||
<string name="language_preference_pair">%1$s → %2$s</string>
|
||||
<string name="search_languages">Buscar idiomas</string>
|
||||
<string name="add_language">Añadir idioma</string>
|
||||
<string name="add_language_pair">Añadir par de idiomas</string>
|
||||
@@ -2408,6 +2455,7 @@
|
||||
<string name="retrying">Reintentando…</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="dismiss">Descartar</string>
|
||||
<string name="share_of">%1$d/%2$d</string>
|
||||
<string name="broadcasting">Difundiendo</string>
|
||||
<string name="broadcasting_name">Difundiendo %1$s</string>
|
||||
<string name="broadcasting_number_events">Difundiendo %1$d eventos…</string>
|
||||
@@ -2504,10 +2552,13 @@
|
||||
<string name="kind_follow_packs">Packs de seguidos</string>
|
||||
<string name="kind_reposts_16">Republicaciones (16)</string>
|
||||
<string name="kind_geohash_follows">Seguidos por geohash</string>
|
||||
<string name="kind_gift_wraps">GiftWraps</string>
|
||||
<string name="kind_git_issue">Incidencia de Git</string>
|
||||
<string name="kind_git_patch">Parche de Git</string>
|
||||
<string name="kind_git_repo">Repositorio de Git</string>
|
||||
<string name="kind_git_reply">Respuesta de Git</string>
|
||||
<string name="kind_git_pr">Solicitud de incorporación</string>
|
||||
<string name="kind_git_pr_update">Actualización de solicitud de incorporación</string>
|
||||
<string name="kind_zap_goals">Objetivos de Zap</string>
|
||||
<string name="kind_hashtag_follows">Seguidos por hashtag</string>
|
||||
<string name="kind_highlights">Destacados</string>
|
||||
@@ -2519,20 +2570,27 @@
|
||||
<string name="kind_named_bookmarks">Marcadores con nombre</string>
|
||||
<string name="kind_live_chats">Chats en directo</string>
|
||||
<string name="kind_live_streams">Transmisiones en directo</string>
|
||||
<string name="kind_zaps">Zaps</string>
|
||||
<string name="kind_nwc_request">Solicitud NWC</string>
|
||||
<string name="kind_nwc_response">Respuesta NWC</string>
|
||||
<string name="kind_private_zaps">Zaps privados</string>
|
||||
<string name="kind_zap_req">Solicitud de Zap</string>
|
||||
<string name="kind_blogs">Blogs</string>
|
||||
<string name="kind_meeting_room">Sala de reuniones</string>
|
||||
<string name="kind_room_presence">Presencia en sala</string>
|
||||
<string name="kind_meeting_space">Espacio de reuniones</string>
|
||||
<string name="kind_profile">Perfil</string>
|
||||
<string name="kind_mute_list">Lista de silenciados</string>
|
||||
<string name="kind_nns">NNS</string>
|
||||
<string name="kind_nip">NIP</string>
|
||||
<string name="kind_nostr_connect">Nostr Connect</string>
|
||||
<string name="kind_dvm_status">Estado DVM</string>
|
||||
<string name="kind_dvm_content_req">Solicitud de contenido DVM</string>
|
||||
<string name="kind_dvm_content_resp">Respuesta de contenido DVM</string>
|
||||
<string name="kind_dvm_user_req">Solicitud de usuario DVM</string>
|
||||
<string name="kind_dvm_user_resp">Respuesta de usuario DVM</string>
|
||||
<string name="kind_ots">OTS</string>
|
||||
<string name="kind_pay_to">Pagar a</string>
|
||||
<string name="kind_people_lists">Listas de personas</string>
|
||||
<string name="kind_pictures">Imágenes</string>
|
||||
<string name="kind_workouts">Entrenamientos</string>
|
||||
@@ -2540,6 +2598,7 @@
|
||||
<string name="kind_zap_poll">Encuesta de Zap</string>
|
||||
<string name="kind_poll">Encuesta</string>
|
||||
<string name="kind_poll_response">Respuesta de encuesta</string>
|
||||
<string name="kind_nip04_dms">Mensajes directos NIP-04</string>
|
||||
<string name="kind_private_relays">Relés privados</string>
|
||||
<string name="kind_proxy_relays">Relés proxy</string>
|
||||
<string name="kind_public_message">Mensaje público</string>
|
||||
@@ -2557,6 +2616,7 @@
|
||||
<string name="kind_user_status">Estado del usuario</string>
|
||||
<string name="kind_notes">Notas</string>
|
||||
<string name="kind_edits">Ediciones</string>
|
||||
<string name="kind_torrents">Torrents</string>
|
||||
<string name="kind_torrent_comments">Comentarios de torrent</string>
|
||||
<string name="kind_trusted_relays">Relés de confianza</string>
|
||||
<string name="kind_trusted_providers">Proveedores de confianza</string>
|
||||
|
||||
@@ -2580,6 +2580,7 @@
|
||||
<string name="git_repo_section_maintainers">Maintainers</string>
|
||||
<string name="git_repo_section_topics">Topics</string>
|
||||
<string name="git_repo_personal_fork">Personal fork</string>
|
||||
<string name="git_repositories">Git Repositories</string>
|
||||
<string name="nsite_title">nSite: %1$s</string>
|
||||
<string name="napplet_card_title">nApplet: %1$s</string>
|
||||
<string name="napplet_card_permissions">Permissions:</string>
|
||||
|
||||
@@ -145,6 +145,14 @@
|
||||
"Slovenian"
|
||||
]
|
||||
},
|
||||
{
|
||||
"user": "BitByBit21",
|
||||
"languages": [
|
||||
"Spanish",
|
||||
"Spanish, Mexico",
|
||||
"Spanish, United States"
|
||||
]
|
||||
},
|
||||
{
|
||||
"user": "hypnotichemionus4",
|
||||
"languages": [
|
||||
@@ -157,12 +165,6 @@
|
||||
"Hungarian"
|
||||
]
|
||||
},
|
||||
{
|
||||
"user": "BitByBit21",
|
||||
"languages": [
|
||||
"Spanish"
|
||||
]
|
||||
},
|
||||
{
|
||||
"user": "greenart7c3",
|
||||
"languages": []
|
||||
|
||||
Reference in New Issue
Block a user