refactor(sonar): merge chained if-else into when
Sonar — convert 'if (A) {…} else if (B) {…} else {…}' chains into
'when { A -> …; B -> …; else -> … }' across 33 files (~44 sites).
The change is mechanically equivalent: same conditions, same branch
order, same short-circuit evaluation, no public-signature impact.
The 70-branch event-type dispatch in ThreadFeedView.kt:594 is intentionally
left as if-else for now — the conversion would balloon to a 280-line diff
with no behavioural change, making review impractical.
This commit is contained in:
@@ -2859,14 +2859,11 @@ class Account(
|
||||
if (event == null) return null
|
||||
|
||||
return if (isWriteable()) {
|
||||
if (event is PrivateDmEvent) {
|
||||
privateDMDecryptionCache.cachedDM(event)
|
||||
} else if (event is LnZapRequestEvent && event.isPrivateZap()) {
|
||||
privateZapsDecryptionCache.cachedPrivateZap(event)?.content
|
||||
} else if (event is DraftWrapEvent) {
|
||||
draftsDecryptionCache.preCachedDraft(event)?.content
|
||||
} else {
|
||||
event.content
|
||||
when {
|
||||
event is PrivateDmEvent -> privateDMDecryptionCache.cachedDM(event)
|
||||
event is LnZapRequestEvent && event.isPrivateZap() -> privateZapsDecryptionCache.cachedPrivateZap(event)?.content
|
||||
event is DraftWrapEvent -> draftsDecryptionCache.preCachedDraft(event)?.content
|
||||
else -> event.content
|
||||
}
|
||||
} else {
|
||||
event.content
|
||||
@@ -2875,22 +2872,30 @@ class Account(
|
||||
|
||||
suspend fun decryptContent(note: Note): String? {
|
||||
val event = note.event
|
||||
return if (event is PrivateDmEvent && isWriteable()) {
|
||||
privateDMDecryptionCache.decryptDM(event)
|
||||
} else if (event is LnZapRequestEvent && isWriteable()) {
|
||||
if (event.isPrivateZap()) {
|
||||
if (isWriteable()) {
|
||||
privateZapsDecryptionCache.decryptPrivateZap(event)?.content
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
event.content
|
||||
return when {
|
||||
event is PrivateDmEvent && isWriteable() -> {
|
||||
privateDMDecryptionCache.decryptDM(event)
|
||||
}
|
||||
|
||||
event is LnZapRequestEvent && isWriteable() -> {
|
||||
if (event.isPrivateZap()) {
|
||||
if (isWriteable()) {
|
||||
privateZapsDecryptionCache.decryptPrivateZap(event)?.content
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
event.content
|
||||
}
|
||||
}
|
||||
|
||||
event is DraftWrapEvent && isWriteable() -> {
|
||||
draftsDecryptionCache.cachedDraft(event)?.content
|
||||
}
|
||||
|
||||
else -> {
|
||||
event?.content
|
||||
}
|
||||
} else if (event is DraftWrapEvent && isWriteable()) {
|
||||
draftsDecryptionCache.cachedDraft(event)?.content
|
||||
} else {
|
||||
event?.content
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,48 +88,56 @@ class ZapPaymentHandler(
|
||||
val zapSplitSetup = noteEvent?.zapSplitSetup()
|
||||
|
||||
val unverifiedZapsToSend =
|
||||
if (!zapSplitSetup.isNullOrEmpty()) {
|
||||
zapSplitSetup.map { setup ->
|
||||
when (setup) {
|
||||
is ZapSplitSetupLnAddress -> {
|
||||
UnverifiedZapSplitSetup(
|
||||
lnAddress = setup.lnAddress,
|
||||
weight = setup.weight,
|
||||
)
|
||||
}
|
||||
when {
|
||||
!zapSplitSetup.isNullOrEmpty() -> {
|
||||
zapSplitSetup.map { setup ->
|
||||
when (setup) {
|
||||
is ZapSplitSetupLnAddress -> {
|
||||
UnverifiedZapSplitSetup(
|
||||
lnAddress = setup.lnAddress,
|
||||
weight = setup.weight,
|
||||
)
|
||||
}
|
||||
|
||||
is ZapSplitSetup -> {
|
||||
val user = LocalCache.checkGetOrCreateUser(setup.pubKeyHex)
|
||||
UnverifiedZapSplitSetup(
|
||||
lnAddress = user?.lnAddress(),
|
||||
weight = setup.weight,
|
||||
relay = setup.relay,
|
||||
user = user,
|
||||
)
|
||||
is ZapSplitSetup -> {
|
||||
val user = LocalCache.checkGetOrCreateUser(setup.pubKeyHex)
|
||||
UnverifiedZapSplitSetup(
|
||||
lnAddress = user?.lnAddress(),
|
||||
weight = setup.weight,
|
||||
relay = setup.relay,
|
||||
user = user,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (noteEvent is LiveActivitiesEvent && noteEvent.hasHost()) {
|
||||
noteEvent.hosts().map {
|
||||
val user = LocalCache.checkGetOrCreateUser(it.pubKey)
|
||||
val lnAddress = user?.lnAddress()
|
||||
UnverifiedZapSplitSetup(lnAddress, relay = it.relayHint, user = user)
|
||||
|
||||
noteEvent is LiveActivitiesEvent && noteEvent.hasHost() -> {
|
||||
noteEvent.hosts().map {
|
||||
val user = LocalCache.checkGetOrCreateUser(it.pubKey)
|
||||
val lnAddress = user?.lnAddress()
|
||||
UnverifiedZapSplitSetup(lnAddress, relay = it.relayHint, user = user)
|
||||
}
|
||||
}
|
||||
} else if (noteEvent is AppDefinitionEvent) {
|
||||
val appLud16 = noteEvent.appMetaData()?.lnAddress()
|
||||
if (appLud16 != null) {
|
||||
listOf(UnverifiedZapSplitSetup(appLud16))
|
||||
} else {
|
||||
val lud16 =
|
||||
note.author?.lnAddress()
|
||||
listOf(UnverifiedZapSplitSetup(lud16))
|
||||
|
||||
noteEvent is AppDefinitionEvent -> {
|
||||
val appLud16 = noteEvent.appMetaData()?.lnAddress()
|
||||
if (appLud16 != null) {
|
||||
listOf(UnverifiedZapSplitSetup(appLud16))
|
||||
} else {
|
||||
val lud16 =
|
||||
note.author?.lnAddress()
|
||||
listOf(UnverifiedZapSplitSetup(lud16))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
listOf(
|
||||
UnverifiedZapSplitSetup(
|
||||
note.author?.lnAddress(),
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
listOf(
|
||||
UnverifiedZapSplitSetup(
|
||||
note.author?.lnAddress(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if (showErrorIfNoLnAddress) {
|
||||
|
||||
@@ -63,18 +63,28 @@ class UrlPreview {
|
||||
val mimeType =
|
||||
response.headers["Content-Type"]?.toMediaType()
|
||||
?: throw IllegalArgumentException("Website returned unknown mimetype: ${response.headers["Content-Type"]}")
|
||||
if (mimeType.type == "text" && mimeType.subtype == "html") {
|
||||
val metaTags = HtmlParser().parseHtml(response.body.source(), mimeType.charset())
|
||||
val data = OpenGraphParser().extractUrlInfo(metaTags)
|
||||
UrlInfoItem(url, data.title, data.description, data.image, mimeType.toString())
|
||||
} else if (mimeType.type == "image") {
|
||||
UrlInfoItem(url, image = url, mimeType = mimeType.toString())
|
||||
} else if (mimeType.type == "video") {
|
||||
UrlInfoItem(url, image = url, mimeType = mimeType.toString())
|
||||
} else if (mimeType.type == "application" && mimeType.subtype == "pdf") {
|
||||
UrlInfoItem(url, image = url, mimeType = mimeType.toString())
|
||||
} else {
|
||||
throw IllegalArgumentException("Website returned unknown encoding for previews: $mimeType")
|
||||
when {
|
||||
mimeType.type == "text" && mimeType.subtype == "html" -> {
|
||||
val metaTags = HtmlParser().parseHtml(response.body.source(), mimeType.charset())
|
||||
val data = OpenGraphParser().extractUrlInfo(metaTags)
|
||||
UrlInfoItem(url, data.title, data.description, data.image, mimeType.toString())
|
||||
}
|
||||
|
||||
mimeType.type == "image" -> {
|
||||
UrlInfoItem(url, image = url, mimeType = mimeType.toString())
|
||||
}
|
||||
|
||||
mimeType.type == "video" -> {
|
||||
UrlInfoItem(url, image = url, mimeType = mimeType.toString())
|
||||
}
|
||||
|
||||
mimeType.type == "application" && mimeType.subtype == "pdf" -> {
|
||||
UrlInfoItem(url, image = url, mimeType = mimeType.toString())
|
||||
}
|
||||
|
||||
else -> {
|
||||
throw IllegalArgumentException("Website returned unknown encoding for previews: $mimeType")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw IllegalArgumentException("Website returned: " + response.code)
|
||||
|
||||
+31
-23
@@ -70,31 +70,39 @@ class RelayProxyClientConnector(
|
||||
RelayServiceInfra(torSettings, torConnection, clearConnection, connectivity, torStatus)
|
||||
}.debounce(100)
|
||||
.onEach {
|
||||
if (it.connectivity is ConnectivityStatus.StartingService) {
|
||||
// ignore
|
||||
} else if (it.connectivity is ConnectivityStatus.Off) {
|
||||
Log.d("ManageRelayServices") { "Connectivity Off: Pausing Relay Services ${it.connectivity}" }
|
||||
if (client.isActive()) {
|
||||
client.disconnect()
|
||||
}
|
||||
if (it.torStatus is TorServiceStatus.Active) {
|
||||
Log.d("ManageRelayServices", "Connectivity off, Tor idle")
|
||||
}
|
||||
} else if (it.connectivity is ConnectivityStatus.Active && !client.isActive()) {
|
||||
Log.d("ManageRelayServices", "Connectivity On: Resuming Relay Services")
|
||||
|
||||
if (it.torStatus is TorServiceStatus.Active) {
|
||||
Log.d("ManageRelayServices", "Connectivity resumed, Tor active")
|
||||
when {
|
||||
it.connectivity is ConnectivityStatus.StartingService -> {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// only calls this if the client is not active. Otherwise goes to the else below
|
||||
client.connect()
|
||||
} else {
|
||||
Log.d("ManageRelayServices", "Relay Services have changed, reconnecting relays that need to")
|
||||
client.reconnect(
|
||||
onlyIfChanged = true,
|
||||
ignoreRetryDelays = true,
|
||||
)
|
||||
it.connectivity is ConnectivityStatus.Off -> {
|
||||
Log.d("ManageRelayServices") { "Connectivity Off: Pausing Relay Services ${it.connectivity}" }
|
||||
if (client.isActive()) {
|
||||
client.disconnect()
|
||||
}
|
||||
if (it.torStatus is TorServiceStatus.Active) {
|
||||
Log.d("ManageRelayServices", "Connectivity off, Tor idle")
|
||||
}
|
||||
}
|
||||
|
||||
it.connectivity is ConnectivityStatus.Active && !client.isActive() -> {
|
||||
Log.d("ManageRelayServices", "Connectivity On: Resuming Relay Services")
|
||||
|
||||
if (it.torStatus is TorServiceStatus.Active) {
|
||||
Log.d("ManageRelayServices", "Connectivity resumed, Tor active")
|
||||
}
|
||||
|
||||
// only calls this if the client is not active. Otherwise goes to the else below
|
||||
client.connect()
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.d("ManageRelayServices", "Relay Services have changed, reconnecting relays that need to")
|
||||
client.reconnect(
|
||||
onlyIfChanged = true,
|
||||
ignoreRetryDelays = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}.onStart {
|
||||
Log.d("ManageRelayServices", "Resuming Relay Services")
|
||||
|
||||
+5
-8
@@ -136,14 +136,11 @@ class BlossomServerResolver(
|
||||
url: String,
|
||||
mimeType: String?,
|
||||
): OkHttpClient =
|
||||
if (mimeType == null) {
|
||||
httpClientBuilder.okHttpClientForPreview(url)
|
||||
} else if (mimeType.startsWith("audio/") || mimeType.startsWith("video/")) {
|
||||
httpClientBuilder.okHttpClientForVideo(url)
|
||||
} else if (mimeType.startsWith("image/")) {
|
||||
httpClientBuilder.okHttpClientForImage(url)
|
||||
} else {
|
||||
httpClientBuilder.okHttpClientForPreview(url)
|
||||
when {
|
||||
mimeType == null -> httpClientBuilder.okHttpClientForPreview(url)
|
||||
mimeType.startsWith("audio/") || mimeType.startsWith("video/") -> httpClientBuilder.okHttpClientForVideo(url)
|
||||
mimeType.startsWith("image/") -> httpClientBuilder.okHttpClientForImage(url)
|
||||
else -> httpClientBuilder.okHttpClientForPreview(url)
|
||||
}
|
||||
|
||||
fun canResolve(scheme: String) = scheme == SCHEME
|
||||
|
||||
@@ -212,32 +212,38 @@ fun EditPostView(
|
||||
if (myUrlPreview != null) {
|
||||
Row(modifier = Modifier.padding(vertical = Size5dp, horizontal = Size10dp)) {
|
||||
if (RichTextParser.isValidURL(myUrlPreview)) {
|
||||
if (RichTextParser.isImageUrl(myUrlPreview)) {
|
||||
AsyncImage(
|
||||
model = myUrlPreview,
|
||||
contentDescription = myUrlPreview,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(top = 4.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = QuoteBorder)
|
||||
.border(
|
||||
1.dp,
|
||||
MaterialTheme.colorScheme.subtleBorder,
|
||||
QuoteBorder,
|
||||
),
|
||||
)
|
||||
} else if (RichTextParser.isVideoUrl(myUrlPreview)) {
|
||||
VideoView(
|
||||
myUrlPreview,
|
||||
mimeType = null,
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
} else {
|
||||
LoadUrlPreview(myUrlPreview, myUrlPreview, null, accountViewModel)
|
||||
when {
|
||||
RichTextParser.isImageUrl(myUrlPreview) -> {
|
||||
AsyncImage(
|
||||
model = myUrlPreview,
|
||||
contentDescription = myUrlPreview,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(top = 4.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(shape = QuoteBorder)
|
||||
.border(
|
||||
1.dp,
|
||||
MaterialTheme.colorScheme.subtleBorder,
|
||||
QuoteBorder,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
RichTextParser.isVideoUrl(myUrlPreview) -> {
|
||||
VideoView(
|
||||
myUrlPreview,
|
||||
mimeType = null,
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
LoadUrlPreview(myUrlPreview, myUrlPreview, null, accountViewModel)
|
||||
}
|
||||
}
|
||||
} else if (RichTextParser.startsWithNIP19Scheme(myUrlPreview)) {
|
||||
val bgColor = MaterialTheme.colorScheme.background
|
||||
|
||||
@@ -188,55 +188,67 @@ class NewMessageTagger(
|
||||
key = key.removePrefix("@")
|
||||
|
||||
try {
|
||||
if (key.startsWith("nsec1", true)) {
|
||||
if (key.length < 63) {
|
||||
return null
|
||||
when {
|
||||
key.startsWith("nsec1", true) -> {
|
||||
if (key.length < 63) {
|
||||
return null
|
||||
}
|
||||
|
||||
val keyB32 = key.substring(0, 63)
|
||||
val restOfWord = key.substring(63)
|
||||
// Converts to npub
|
||||
val pubkey =
|
||||
Nip19Parser.uriToRoute(Nip01Crypto.pubKeyCreate(keyB32.bechToBytes()).toNpub()) ?: return null
|
||||
|
||||
return DirtyKeyInfo(pubkey, restOfWord.ifEmpty { null })
|
||||
}
|
||||
|
||||
val keyB32 = key.substring(0, 63)
|
||||
val restOfWord = key.substring(63)
|
||||
// Converts to npub
|
||||
val pubkey =
|
||||
Nip19Parser.uriToRoute(Nip01Crypto.pubKeyCreate(keyB32.bechToBytes()).toNpub()) ?: return null
|
||||
key.startsWith("npub1", true) -> {
|
||||
if (key.length < 63) {
|
||||
return null
|
||||
}
|
||||
|
||||
return DirtyKeyInfo(pubkey, restOfWord.ifEmpty { null })
|
||||
} else if (key.startsWith("npub1", true)) {
|
||||
if (key.length < 63) {
|
||||
return null
|
||||
val keyB32 = key.substring(0, 63)
|
||||
val restOfWord = key.substring(63)
|
||||
|
||||
val pubkey = Nip19Parser.uriToRoute(keyB32) ?: return null
|
||||
|
||||
return DirtyKeyInfo(pubkey, restOfWord.ifEmpty { null })
|
||||
}
|
||||
|
||||
val keyB32 = key.substring(0, 63)
|
||||
val restOfWord = key.substring(63)
|
||||
key.startsWith("note1", true) -> {
|
||||
if (key.length < 63) {
|
||||
return null
|
||||
}
|
||||
|
||||
val pubkey = Nip19Parser.uriToRoute(keyB32) ?: return null
|
||||
val keyB32 = key.substring(0, 63)
|
||||
val restOfWord = key.substring(63)
|
||||
|
||||
return DirtyKeyInfo(pubkey, restOfWord.ifEmpty { null })
|
||||
} else if (key.startsWith("note1", true)) {
|
||||
if (key.length < 63) {
|
||||
return null
|
||||
val noteId = Nip19Parser.uriToRoute(keyB32) ?: return null
|
||||
|
||||
return DirtyKeyInfo(noteId, restOfWord.ifEmpty { null })
|
||||
}
|
||||
|
||||
val keyB32 = key.substring(0, 63)
|
||||
val restOfWord = key.substring(63)
|
||||
key.startsWith("nprofile", true) -> {
|
||||
val pubkeyRelay = Nip19Parser.uriToRoute(key) ?: return null
|
||||
|
||||
val noteId = Nip19Parser.uriToRoute(keyB32) ?: return null
|
||||
return DirtyKeyInfo(pubkeyRelay, pubkeyRelay.additionalChars)
|
||||
}
|
||||
|
||||
return DirtyKeyInfo(noteId, restOfWord.ifEmpty { null })
|
||||
} else if (key.startsWith("nprofile", true)) {
|
||||
val pubkeyRelay = Nip19Parser.uriToRoute(key) ?: return null
|
||||
key.startsWith("nevent1", true) -> {
|
||||
val noteRelayId = Nip19Parser.uriToRoute(key) ?: return null
|
||||
|
||||
return DirtyKeyInfo(pubkeyRelay, pubkeyRelay.additionalChars)
|
||||
} else if (key.startsWith("nevent1", true)) {
|
||||
val noteRelayId = Nip19Parser.uriToRoute(key) ?: return null
|
||||
return DirtyKeyInfo(noteRelayId, noteRelayId.additionalChars)
|
||||
}
|
||||
|
||||
return DirtyKeyInfo(noteRelayId, noteRelayId.additionalChars)
|
||||
} else if (key.startsWith("naddr1", true)) {
|
||||
val address = Nip19Parser.uriToRoute(key) ?: return null
|
||||
key.startsWith("naddr1", true) -> {
|
||||
val address = Nip19Parser.uriToRoute(key) ?: return null
|
||||
|
||||
return DirtyKeyInfo(
|
||||
address,
|
||||
address.additionalChars,
|
||||
) // no way to know when they address ends and dirt begins
|
||||
return DirtyKeyInfo(
|
||||
address,
|
||||
address.additionalChars,
|
||||
) // no way to know when they address ends and dirt begins
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
|
||||
+58
-48
@@ -113,51 +113,69 @@ fun ShowImageGallery(
|
||||
media: SelectedMedia,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
if (media.isImage() == true) {
|
||||
AsyncImage(
|
||||
model = media.uri.toString(),
|
||||
contentDescription = media.uri.toString(),
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
)
|
||||
} else if (media.isAudio() == true) {
|
||||
FilePreviewPlaceholder(
|
||||
icon = MaterialSymbols.AudioFile,
|
||||
label = media.mimeType ?: "audio/*",
|
||||
)
|
||||
} else if (media.isDocument()) {
|
||||
FilePreviewPlaceholder(
|
||||
icon = MaterialSymbols.PictureAsPdf,
|
||||
label = media.mimeType ?: "application/pdf",
|
||||
)
|
||||
} else if (media.isVideo() == true && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val context = LocalContext.current
|
||||
when {
|
||||
media.isImage() == true -> {
|
||||
AsyncImage(
|
||||
model = media.uri.toString(),
|
||||
contentDescription = media.uri.toString(),
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = media) {
|
||||
launch(Dispatchers.IO) {
|
||||
try {
|
||||
bitmap = createVideoThumb(context, media.uri)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.w("NewPostView", "Couldn't create thumbnail, but the video can be uploaded", e)
|
||||
media.isAudio() == true -> {
|
||||
FilePreviewPlaceholder(
|
||||
icon = MaterialSymbols.AudioFile,
|
||||
label = media.mimeType ?: "audio/*",
|
||||
)
|
||||
}
|
||||
|
||||
media.isDocument() -> {
|
||||
FilePreviewPlaceholder(
|
||||
icon = MaterialSymbols.PictureAsPdf,
|
||||
label = media.mimeType ?: "application/pdf",
|
||||
)
|
||||
}
|
||||
|
||||
media.isVideo() == true && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
|
||||
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(key1 = media) {
|
||||
launch(Dispatchers.IO) {
|
||||
try {
|
||||
bitmap = createVideoThumb(context, media.uri)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.w("NewPostView", "Couldn't create thumbnail, but the video can be uploaded", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitmap != null) {
|
||||
bitmap?.let {
|
||||
Image(
|
||||
bitmap = it.asImageBitmap(),
|
||||
contentDescription = "some useful description",
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
VideoView(
|
||||
videoUri = media.uri.toString(),
|
||||
mimeType = media.mimeType,
|
||||
roundedCorner = false,
|
||||
contentScale = ContentScale.Crop,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (bitmap != null) {
|
||||
bitmap?.let {
|
||||
Image(
|
||||
bitmap = it.asImageBitmap(),
|
||||
contentDescription = "some useful description",
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
else -> {
|
||||
VideoView(
|
||||
videoUri = media.uri.toString(),
|
||||
mimeType = media.mimeType,
|
||||
@@ -166,14 +184,6 @@ fun ShowImageGallery(
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
VideoView(
|
||||
videoUri = media.uri.toString(),
|
||||
mimeType = media.mimeType,
|
||||
roundedCorner = false,
|
||||
contentScale = ContentScale.Crop,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,34 +95,42 @@ fun RenderLoaded(
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
if (state.previewInfo.mimeType.startsWith("image")) {
|
||||
Box(modifier = HalfVertPadding) {
|
||||
ZoomableContentView(
|
||||
content = MediaUrlImage(url, uri = callbackUri),
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
when {
|
||||
state.previewInfo.mimeType.startsWith("image") -> {
|
||||
Box(modifier = HalfVertPadding) {
|
||||
ZoomableContentView(
|
||||
content = MediaUrlImage(url, uri = callbackUri),
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (state.previewInfo.mimeType.startsWith("video")) {
|
||||
Box(modifier = HalfVertPadding) {
|
||||
ZoomableContentView(
|
||||
content = MediaUrlVideo(url, uri = callbackUri),
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
|
||||
state.previewInfo.mimeType.startsWith("video") -> {
|
||||
Box(modifier = HalfVertPadding) {
|
||||
ZoomableContentView(
|
||||
content = MediaUrlVideo(url, uri = callbackUri),
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (state.previewInfo.mimeType.startsWith("application/pdf")) {
|
||||
Box(modifier = HalfVertPadding) {
|
||||
ZoomableContentView(
|
||||
content = MediaUrlPdf(url, uri = callbackUri, mimeType = state.previewInfo.mimeType),
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
|
||||
state.previewInfo.mimeType.startsWith("application/pdf") -> {
|
||||
Box(modifier = HalfVertPadding) {
|
||||
ZoomableContentView(
|
||||
content = MediaUrlPdf(url, uri = callbackUri, mimeType = state.previewInfo.mimeType),
|
||||
roundedCorner = true,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
UrlPreviewCard(url, state.previewInfo)
|
||||
}
|
||||
} else {
|
||||
UrlPreviewCard(url, state.previewInfo)
|
||||
}
|
||||
}
|
||||
|
||||
+40
-32
@@ -293,40 +293,48 @@ private fun handleZapClick(
|
||||
|
||||
val choices = zapAmountChoices ?: accountViewModel.zapAmountChoices()
|
||||
|
||||
if (choices.isEmpty()) {
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.error_dialog_zap_error),
|
||||
stringRes(context, R.string.no_zap_amount_setup_long_press_to_change),
|
||||
)
|
||||
} else if (!accountViewModel.isWriteable()) {
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.error_dialog_zap_error),
|
||||
stringRes(context, R.string.login_with_a_private_key_to_be_able_to_send_zaps),
|
||||
)
|
||||
} else if (choices.size == 1) {
|
||||
val amount = choices.first()
|
||||
|
||||
if (amount > 1100 || zapAmountChoices != null) {
|
||||
onZapStarts()
|
||||
accountViewModel.zap(
|
||||
baseNote,
|
||||
amount * 1000,
|
||||
null,
|
||||
"",
|
||||
context,
|
||||
showErrorIfNoLnAddress = false,
|
||||
onError = onError,
|
||||
onProgress = { onZappingProgress(it) },
|
||||
onPayViaIntent = onPayViaIntent,
|
||||
when {
|
||||
choices.isEmpty() -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.error_dialog_zap_error),
|
||||
stringRes(context, R.string.no_zap_amount_setup_long_press_to_change),
|
||||
)
|
||||
} else {
|
||||
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
||||
}
|
||||
} else {
|
||||
if (choices.any { it > 1100 } || zapAmountChoices != null) {
|
||||
onMultipleChoices(choices)
|
||||
} else {
|
||||
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
||||
|
||||
!accountViewModel.isWriteable() -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
stringRes(context, R.string.error_dialog_zap_error),
|
||||
stringRes(context, R.string.login_with_a_private_key_to_be_able_to_send_zaps),
|
||||
)
|
||||
}
|
||||
|
||||
choices.size == 1 -> {
|
||||
val amount = choices.first()
|
||||
|
||||
if (amount > 1100 || zapAmountChoices != null) {
|
||||
onZapStarts()
|
||||
accountViewModel.zap(
|
||||
baseNote,
|
||||
amount * 1000,
|
||||
null,
|
||||
"",
|
||||
context,
|
||||
showErrorIfNoLnAddress = false,
|
||||
onError = onError,
|
||||
onProgress = { onZappingProgress(it) },
|
||||
onPayViaIntent = onPayViaIntent,
|
||||
)
|
||||
} else {
|
||||
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (choices.any { it > 1100 } || zapAmountChoices != null) {
|
||||
onMultipleChoices(choices)
|
||||
} else {
|
||||
onMultipleChoices(listOf(1000L, 5_000L, 10_000L))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,25 +67,31 @@ fun WatchBlockAndReport(
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
if (showAnyway.value) {
|
||||
normalNote(true)
|
||||
} else if (!isHidden.isPostHidden) {
|
||||
if (isHidden.isAcceptable) {
|
||||
normalNote(isHidden.canPreview)
|
||||
} else {
|
||||
HiddenNote(
|
||||
isHidden.relevantReports,
|
||||
isHidden.isHiddenAuthor,
|
||||
accountViewModel,
|
||||
modifier,
|
||||
nav,
|
||||
onClick = { showAnyway.value = true },
|
||||
)
|
||||
when {
|
||||
showAnyway.value -> {
|
||||
normalNote(true)
|
||||
}
|
||||
} else if (showHiddenWarning) {
|
||||
// if it is a quoted or boosted note, how the hidden warning.
|
||||
HiddenNoteByMe {
|
||||
showAnyway.value = true
|
||||
|
||||
!isHidden.isPostHidden -> {
|
||||
if (isHidden.isAcceptable) {
|
||||
normalNote(isHidden.canPreview)
|
||||
} else {
|
||||
HiddenNote(
|
||||
isHidden.relevantReports,
|
||||
isHidden.isHiddenAuthor,
|
||||
accountViewModel,
|
||||
modifier,
|
||||
nav,
|
||||
onClick = { showAnyway.value = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
showHiddenWarning -> {
|
||||
// if it is a quoted or boosted note, how the hidden warning.
|
||||
HiddenNoteByMe {
|
||||
showAnyway.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
-61
@@ -293,78 +293,86 @@ fun DisplayStatusInner(
|
||||
)
|
||||
}
|
||||
|
||||
if (url != null) {
|
||||
val uri = LocalUriHandler.current
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
modifier = Size15Modifier,
|
||||
onClick = { runCatching { uri.openUri(url.trim()) } },
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
when {
|
||||
url != null -> {
|
||||
val uri = LocalUriHandler.current
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
}
|
||||
} else if (nostrATag != null) {
|
||||
LoadAddressableNote(nostrATag, accountViewModel) { note ->
|
||||
if (note != null) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
onClick = { runCatching { uri.openUri(url.trim()) } },
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
modifier = Size15Modifier,
|
||||
onClick = {
|
||||
routeFor(
|
||||
note,
|
||||
accountViewModel.account,
|
||||
)?.let { nav.nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
nostrATag != null -> {
|
||||
LoadAddressableNote(nostrATag, accountViewModel) { note ->
|
||||
if (note != null) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
onClick = {
|
||||
routeFor(
|
||||
note,
|
||||
accountViewModel.account,
|
||||
)?.let { nav.nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nostrETag != null) {
|
||||
LoadNote(baseNoteHex = nostrETag.eventId, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
modifier = Size15Modifier,
|
||||
onClick = {
|
||||
routeFor(
|
||||
it,
|
||||
accountViewModel.account,
|
||||
)?.let { nav.nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
|
||||
nostrETag != null -> {
|
||||
LoadNote(baseNoteHex = nostrETag.eventId, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
onClick = {
|
||||
routeFor(
|
||||
it,
|
||||
accountViewModel.account,
|
||||
)?.let { nav.nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nostrPTag != null) {
|
||||
LoadUser(baseUserHex = nostrPTag, accountViewModel) { user ->
|
||||
if (user != null) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
modifier = Size15Modifier,
|
||||
onClick = { nav.nav(routeForUser(nostrPTag)) },
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
|
||||
nostrPTag != null -> {
|
||||
LoadUser(baseUserHex = nostrPTag, accountViewModel) { user ->
|
||||
if (user != null) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
IconButton(
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
onClick = { nav.nav(routeForUser(nostrPTag)) },
|
||||
) {
|
||||
Icon(
|
||||
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||
null,
|
||||
modifier = Size15Modifier,
|
||||
tint = MaterialTheme.colorScheme.lessImportantLink,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,14 +116,11 @@ val graspLink = { graspNumber: String ->
|
||||
|
||||
val externalLinkForNote = { note: Note ->
|
||||
if (note is AddressableNote) {
|
||||
if (note.event?.bountyBaseReward() != null) {
|
||||
"https://nostrbounties.com/b/${note.toNAddr()}"
|
||||
} else if (note.event is PeopleListEvent) {
|
||||
"https://listr.lol/a/${note.toNAddr()}"
|
||||
} else if (note.event is FollowListEvent) {
|
||||
"https://following.space/d/${note.address.dTag}?p=${note.address.pubKeyHex}"
|
||||
} else {
|
||||
njumpLink(note.toNAddr())
|
||||
when {
|
||||
note.event?.bountyBaseReward() != null -> "https://nostrbounties.com/b/${note.toNAddr()}"
|
||||
note.event is PeopleListEvent -> "https://listr.lol/a/${note.toNAddr()}"
|
||||
note.event is FollowListEvent -> "https://following.space/d/${note.address.dTag}?p=${note.address.pubKeyHex}"
|
||||
else -> njumpLink(note.toNAddr())
|
||||
}
|
||||
} else {
|
||||
njumpLink(note.toNEvent())
|
||||
|
||||
@@ -1146,20 +1146,28 @@ private fun likeClick(
|
||||
}
|
||||
|
||||
val choices = accountViewModel.reactionChoices()
|
||||
if (choices.isEmpty()) {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.no_reactions_setup,
|
||||
R.string.no_reaction_type_setup_long_press_to_change,
|
||||
)
|
||||
} else if (!accountViewModel.isWriteable()) {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.read_only_user,
|
||||
R.string.login_with_a_private_key_to_like_posts,
|
||||
)
|
||||
} else if (choices.size == 1) {
|
||||
onWantsToSignReaction()
|
||||
} else if (choices.size > 1) {
|
||||
onMultipleChoices()
|
||||
when {
|
||||
choices.isEmpty() -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.no_reactions_setup,
|
||||
R.string.no_reaction_type_setup_long_press_to_change,
|
||||
)
|
||||
}
|
||||
|
||||
!accountViewModel.isWriteable() -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.read_only_user,
|
||||
R.string.login_with_a_private_key_to_like_posts,
|
||||
)
|
||||
}
|
||||
|
||||
choices.size == 1 -> {
|
||||
onWantsToSignReaction()
|
||||
}
|
||||
|
||||
choices.size > 1 -> {
|
||||
onMultipleChoices()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1369,27 +1377,35 @@ fun zapClick(
|
||||
|
||||
val choices = accountViewModel.zapAmountChoices()
|
||||
|
||||
if (choices.isEmpty()) {
|
||||
onCustomAmount()
|
||||
} else if (!accountViewModel.isWriteable()) {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.error_dialog_zap_error,
|
||||
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
|
||||
)
|
||||
} else if (choices.size == 1) {
|
||||
onZapStarts()
|
||||
accountViewModel.zap(
|
||||
baseNote,
|
||||
choices.first() * 1000,
|
||||
null,
|
||||
"",
|
||||
context,
|
||||
onError = onError,
|
||||
onProgress = { onZappingProgress(it) },
|
||||
onPayViaIntent = onPayViaIntent,
|
||||
)
|
||||
} else if (choices.size > 1) {
|
||||
onMultipleChoices()
|
||||
when {
|
||||
choices.isEmpty() -> {
|
||||
onCustomAmount()
|
||||
}
|
||||
|
||||
!accountViewModel.isWriteable() -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.error_dialog_zap_error,
|
||||
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
|
||||
)
|
||||
}
|
||||
|
||||
choices.size == 1 -> {
|
||||
onZapStarts()
|
||||
accountViewModel.zap(
|
||||
baseNote,
|
||||
choices.first() * 1000,
|
||||
null,
|
||||
"",
|
||||
context,
|
||||
onError = onError,
|
||||
onProgress = { onZappingProgress(it) },
|
||||
onPayViaIntent = onPayViaIntent,
|
||||
)
|
||||
}
|
||||
|
||||
choices.size > 1 -> {
|
||||
onMultipleChoices()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,34 +56,45 @@ fun timeAgo(
|
||||
|
||||
val timeDifference = TimeUtils.now() - time
|
||||
|
||||
return if (timeDifference > TimeUtils.ONE_YEAR) {
|
||||
// Dec 12, 2022
|
||||
return when {
|
||||
timeDifference > TimeUtils.ONE_YEAR -> {
|
||||
// Dec 12, 2022
|
||||
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
prefix + yearFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
prefix + yearFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_MONTH) {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
timeDifference > TimeUtils.ONE_MONTH -> {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
prefix + monthFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
prefix + monthFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_DAY) {
|
||||
// 2 days
|
||||
prefix + (timeDifference / TimeUtils.ONE_DAY).toString() + stringRes(context, days)
|
||||
} else if (timeDifference > TimeUtils.ONE_HOUR) {
|
||||
prefix + (timeDifference / TimeUtils.ONE_HOUR).toString() + stringRes(context, hours)
|
||||
} else if (timeDifference > TimeUtils.ONE_MINUTE) {
|
||||
prefix + (timeDifference / TimeUtils.ONE_MINUTE).toString() + stringRes(context, minutes)
|
||||
} else {
|
||||
prefix + stringRes(context, seconds)
|
||||
timeDifference > TimeUtils.ONE_DAY -> {
|
||||
prefix + (timeDifference / TimeUtils.ONE_DAY).toString() + stringRes(context, days)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_HOUR -> {
|
||||
prefix + (timeDifference / TimeUtils.ONE_HOUR).toString() + stringRes(context, hours)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_MINUTE -> {
|
||||
prefix + (timeDifference / TimeUtils.ONE_MINUTE).toString() + stringRes(context, minutes)
|
||||
}
|
||||
|
||||
else -> {
|
||||
prefix + stringRes(context, seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,34 +107,45 @@ fun timeAgoNoDot(
|
||||
|
||||
val timeDifference = TimeUtils.now() - time
|
||||
|
||||
return if (timeDifference > TimeUtils.ONE_YEAR) {
|
||||
// Dec 12, 2022
|
||||
return when {
|
||||
timeDifference > TimeUtils.ONE_YEAR -> {
|
||||
// Dec 12, 2022
|
||||
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
yearFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
yearFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_MONTH) {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
timeDifference > TimeUtils.ONE_MONTH -> {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
monthFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
monthFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_DAY) {
|
||||
// 2 days
|
||||
(timeDifference / TimeUtils.ONE_DAY).toString() + stringRes(context, R.string.d)
|
||||
} else if (timeDifference > TimeUtils.ONE_HOUR) {
|
||||
(timeDifference / TimeUtils.ONE_HOUR).toString() + stringRes(context, R.string.h)
|
||||
} else if (timeDifference > TimeUtils.ONE_MINUTE) {
|
||||
(timeDifference / TimeUtils.ONE_MINUTE).toString() + stringRes(context, R.string.m)
|
||||
} else {
|
||||
stringRes(context, R.string.now)
|
||||
timeDifference > TimeUtils.ONE_DAY -> {
|
||||
(timeDifference / TimeUtils.ONE_DAY).toString() + stringRes(context, R.string.d)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_HOUR -> {
|
||||
(timeDifference / TimeUtils.ONE_HOUR).toString() + stringRes(context, R.string.h)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_MINUTE -> {
|
||||
(timeDifference / TimeUtils.ONE_MINUTE).toString() + stringRes(context, R.string.m)
|
||||
}
|
||||
|
||||
else -> {
|
||||
stringRes(context, R.string.now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,34 +158,45 @@ fun timeAgoNoDotNoDay(
|
||||
|
||||
val timeDifference = TimeUtils.now() - time
|
||||
|
||||
return if (timeDifference > TimeUtils.ONE_YEAR) {
|
||||
// Dec 12, 2022
|
||||
return when {
|
||||
timeDifference > TimeUtils.ONE_YEAR -> {
|
||||
// Dec 12, 2022
|
||||
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearNoDayFormatter = SimpleDateFormat(YEAR_NO_DAY_DATE_FORMAT, locale)
|
||||
monthNoDayFormatter = SimpleDateFormat(MONTH_NO_DAY_DATE_FORMAT, locale)
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearNoDayFormatter = SimpleDateFormat(YEAR_NO_DAY_DATE_FORMAT, locale)
|
||||
monthNoDayFormatter = SimpleDateFormat(MONTH_NO_DAY_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
yearNoDayFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
yearNoDayFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_MONTH) {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearNoDayFormatter = SimpleDateFormat(YEAR_NO_DAY_DATE_FORMAT, locale)
|
||||
monthNoDayFormatter = SimpleDateFormat(MONTH_NO_DAY_DATE_FORMAT, locale)
|
||||
timeDifference > TimeUtils.ONE_MONTH -> {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearNoDayFormatter = SimpleDateFormat(YEAR_NO_DAY_DATE_FORMAT, locale)
|
||||
monthNoDayFormatter = SimpleDateFormat(MONTH_NO_DAY_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
monthNoDayFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
monthNoDayFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_DAY) {
|
||||
// 2 days
|
||||
(timeDifference / TimeUtils.ONE_DAY).toString() + stringRes(context, R.string.d)
|
||||
} else if (timeDifference > TimeUtils.ONE_HOUR) {
|
||||
(timeDifference / TimeUtils.ONE_HOUR).toString() + stringRes(context, R.string.h)
|
||||
} else if (timeDifference > TimeUtils.ONE_MINUTE) {
|
||||
(timeDifference / TimeUtils.ONE_MINUTE).toString() + stringRes(context, R.string.m)
|
||||
} else {
|
||||
stringRes(context, R.string.now)
|
||||
timeDifference > TimeUtils.ONE_DAY -> {
|
||||
(timeDifference / TimeUtils.ONE_DAY).toString() + stringRes(context, R.string.d)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_HOUR -> {
|
||||
(timeDifference / TimeUtils.ONE_HOUR).toString() + stringRes(context, R.string.h)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_MINUTE -> {
|
||||
(timeDifference / TimeUtils.ONE_MINUTE).toString() + stringRes(context, R.string.m)
|
||||
}
|
||||
|
||||
else -> {
|
||||
stringRes(context, R.string.now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,34 +209,45 @@ fun timeAheadNoDot(
|
||||
|
||||
val timeDifference = time - TimeUtils.now()
|
||||
|
||||
return if (timeDifference > TimeUtils.ONE_YEAR) {
|
||||
// Dec 12, 2022
|
||||
return when {
|
||||
timeDifference > TimeUtils.ONE_YEAR -> {
|
||||
// Dec 12, 2022
|
||||
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
yearFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
yearFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_MONTH) {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
timeDifference > TimeUtils.ONE_MONTH -> {
|
||||
// Dec 12
|
||||
if (locale != Locale.getDefault()) {
|
||||
locale = Locale.getDefault()
|
||||
yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
|
||||
monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
|
||||
}
|
||||
|
||||
monthFormatter.format(time * 1000)
|
||||
}
|
||||
|
||||
monthFormatter.format(time * 1000)
|
||||
} else if (timeDifference > TimeUtils.ONE_DAY) {
|
||||
// 2 days
|
||||
round(timeDifference / TimeUtils.ONE_DAY.toFloat()).toInt().toString() + stringRes(context, R.string.d)
|
||||
} else if (timeDifference > TimeUtils.ONE_HOUR) {
|
||||
round(timeDifference / TimeUtils.ONE_HOUR.toFloat()).toInt().toString() + stringRes(context, R.string.h)
|
||||
} else if (timeDifference > TimeUtils.ONE_MINUTE) {
|
||||
round(timeDifference / TimeUtils.ONE_MINUTE.toFloat()).toInt().toString() + stringRes(context, R.string.m)
|
||||
} else {
|
||||
stringRes(context, R.string.now)
|
||||
timeDifference > TimeUtils.ONE_DAY -> {
|
||||
round(timeDifference / TimeUtils.ONE_DAY.toFloat()).toInt().toString() + stringRes(context, R.string.d)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_HOUR -> {
|
||||
round(timeDifference / TimeUtils.ONE_HOUR.toFloat()).toInt().toString() + stringRes(context, R.string.h)
|
||||
}
|
||||
|
||||
timeDifference > TimeUtils.ONE_MINUTE -> {
|
||||
round(timeDifference / TimeUtils.ONE_MINUTE.toFloat()).toInt().toString() + stringRes(context, R.string.m)
|
||||
}
|
||||
|
||||
else -> {
|
||||
stringRes(context, R.string.now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -547,59 +547,69 @@ fun ZapVote(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple24dp,
|
||||
onClick = {
|
||||
if (!accountViewModel.isWriteable()) {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.read_only_user,
|
||||
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
|
||||
)
|
||||
} else if (pollViewModel.isPollClosed()) {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.poll_unable_to_vote,
|
||||
R.string.poll_is_closed_explainer,
|
||||
)
|
||||
} else if (isLoggedUser) {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.poll_unable_to_vote,
|
||||
R.string.poll_author_no_vote,
|
||||
)
|
||||
} else if (pollViewModel.isVoteAmountAtomic() && poolOption.zappedByLoggedIn.value) {
|
||||
// only allow one vote per option when min==max, i.e. atomic vote amount specified
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.poll_unable_to_vote,
|
||||
R.string.one_vote_per_user_on_atomic_votes,
|
||||
)
|
||||
return@combinedClickable
|
||||
} else if (
|
||||
when {
|
||||
!accountViewModel.isWriteable() -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.read_only_user,
|
||||
R.string.login_with_a_private_key_to_be_able_to_send_zaps,
|
||||
)
|
||||
}
|
||||
|
||||
pollViewModel.isPollClosed() -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.poll_unable_to_vote,
|
||||
R.string.poll_is_closed_explainer,
|
||||
)
|
||||
}
|
||||
|
||||
isLoggedUser -> {
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.poll_unable_to_vote,
|
||||
R.string.poll_author_no_vote,
|
||||
)
|
||||
}
|
||||
|
||||
pollViewModel.isVoteAmountAtomic() && poolOption.zappedByLoggedIn.value -> {
|
||||
// only allow one vote per option when min==max, i.e. atomic vote amount specified
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.poll_unable_to_vote,
|
||||
R.string.one_vote_per_user_on_atomic_votes,
|
||||
)
|
||||
return@combinedClickable
|
||||
}
|
||||
|
||||
accountViewModel.zapAmountChoices().size == 1 &&
|
||||
pollViewModel.isValidInputVoteAmount(accountViewModel.zapAmountChoices().first())
|
||||
) {
|
||||
accountViewModel.zap(
|
||||
baseNote,
|
||||
accountViewModel.zapAmountChoices().first() * 1000,
|
||||
poolOption.option,
|
||||
"",
|
||||
context,
|
||||
onError = { title, message, user ->
|
||||
zappingProgress = 0f
|
||||
showErrorMessageDialog = StringToastMsg(title, message)
|
||||
},
|
||||
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
|
||||
onPayViaIntent = {
|
||||
if (it.size == 1) {
|
||||
val payable = it.first()
|
||||
payViaIntent(payable.invoice, context, { }) { error ->
|
||||
zappingProgress = 0f
|
||||
showErrorMessageDialog = StringToastMsg(stringRes(context, R.string.error_dialog_zap_error), error)
|
||||
pollViewModel.isValidInputVoteAmount(accountViewModel.zapAmountChoices().first()) -> {
|
||||
accountViewModel.zap(
|
||||
baseNote,
|
||||
accountViewModel.zapAmountChoices().first() * 1000,
|
||||
poolOption.option,
|
||||
"",
|
||||
context,
|
||||
onError = { title, message, user ->
|
||||
zappingProgress = 0f
|
||||
showErrorMessageDialog = StringToastMsg(title, message)
|
||||
},
|
||||
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
|
||||
onPayViaIntent = {
|
||||
if (it.size == 1) {
|
||||
val payable = it.first()
|
||||
payViaIntent(payable.invoice, context, { }) { error ->
|
||||
zappingProgress = 0f
|
||||
showErrorMessageDialog = StringToastMsg(stringRes(context, R.string.error_dialog_zap_error), error)
|
||||
}
|
||||
} else {
|
||||
val uid = Uuid.random().toString()
|
||||
accountViewModel.tempManualPaymentCache.put(uid, it)
|
||||
nav.nav(Route.ManualZapSplitPayment(uid))
|
||||
}
|
||||
} else {
|
||||
val uid = Uuid.random().toString()
|
||||
accountViewModel.tempManualPaymentCache.put(uid, it)
|
||||
nav.nav(Route.ManualZapSplitPayment(uid))
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
wantsToZap = true
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
wantsToZap = true
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -138,14 +138,11 @@ class PollNoteViewModel : ViewModel() {
|
||||
} == true
|
||||
|
||||
fun voteAmountPlaceHolderText(sats: String): String =
|
||||
if (valueMinimum == null && valueMaximum == null) {
|
||||
sats
|
||||
} else if (valueMinimum == null) {
|
||||
"1—$valueMaximum $sats"
|
||||
} else if (valueMaximum == null) {
|
||||
">$valueMinimum $sats"
|
||||
} else {
|
||||
"$valueMinimum—$valueMaximum $sats"
|
||||
when {
|
||||
valueMinimum == null && valueMaximum == null -> sats
|
||||
valueMinimum == null -> "1—$valueMaximum $sats"
|
||||
valueMaximum == null -> ">$valueMinimum $sats"
|
||||
else -> "$valueMinimum—$valueMaximum $sats"
|
||||
}
|
||||
|
||||
fun inputVoteAmountLong(textAmount: String) =
|
||||
@@ -160,46 +157,66 @@ class PollNoteViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun isValidInputVoteAmount(amount: BigDecimal?): Boolean {
|
||||
if (amount == null) {
|
||||
return false
|
||||
} else if (valueMinimum == null && valueMaximum == null) {
|
||||
if (amount > BigDecimal.ZERO) {
|
||||
return true
|
||||
when {
|
||||
amount == null -> {
|
||||
return false
|
||||
}
|
||||
} else if (valueMinimum == null) {
|
||||
if (amount > BigDecimal.ZERO && amount <= valueMaximumBD!!) {
|
||||
return true
|
||||
|
||||
valueMinimum == null && valueMaximum == null -> {
|
||||
if (amount > BigDecimal.ZERO) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else if (valueMaximum == null) {
|
||||
if (amount >= valueMinimumBD!!) {
|
||||
return true
|
||||
|
||||
valueMinimum == null -> {
|
||||
if (amount > BigDecimal.ZERO && amount <= valueMaximumBD!!) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((valueMinimumBD!! <= amount) && (amount <= valueMaximumBD!!)) {
|
||||
return true
|
||||
|
||||
valueMaximum == null -> {
|
||||
if (amount >= valueMinimumBD!!) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
if ((valueMinimumBD!! <= amount) && (amount <= valueMaximumBD!!)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun isValidInputVoteAmount(amount: Long?): Boolean {
|
||||
if (amount == null) {
|
||||
return false
|
||||
} else if (valueMinimum == null && valueMaximum == null) {
|
||||
if (amount > 0) {
|
||||
return true
|
||||
when {
|
||||
amount == null -> {
|
||||
return false
|
||||
}
|
||||
} else if (valueMinimum == null) {
|
||||
if (amount > 0 && amount <= valueMaximum!!) {
|
||||
return true
|
||||
|
||||
valueMinimum == null && valueMaximum == null -> {
|
||||
if (amount > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else if (valueMaximum == null) {
|
||||
if (amount >= valueMinimum!!) {
|
||||
return true
|
||||
|
||||
valueMinimum == null -> {
|
||||
if (amount > 0 && amount <= valueMaximum!!) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((valueMinimum!! <= amount) && (amount <= valueMaximum!!)) {
|
||||
return true
|
||||
|
||||
valueMaximum == null -> {
|
||||
if (amount >= valueMinimum!!) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
if ((valueMinimum!! <= amount) && (amount <= valueMaximum!!)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
+69
-51
@@ -60,40 +60,52 @@ fun PreviewUrl(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
if (RichTextParser.isValidURL(myUrlPreview)) {
|
||||
if (RichTextParser.isImageUrl(myUrlPreview)) {
|
||||
AsyncImage(
|
||||
model = myUrlPreview,
|
||||
contentDescription = myUrlPreview,
|
||||
contentScale = ContentScale.FillHeight,
|
||||
modifier = Modifier.fillMaxHeight().aspectRatio(1f),
|
||||
)
|
||||
} else if (RichTextParser.isVideoUrl(myUrlPreview)) {
|
||||
VideoView(
|
||||
myUrlPreview,
|
||||
mimeType = null,
|
||||
roundedCorner = false,
|
||||
gallery = false,
|
||||
contentScale = ContentScale.FillHeight,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
} else {
|
||||
MyLoadUrlPreviewDirect(myUrlPreview, myUrlPreview, accountViewModel)
|
||||
}
|
||||
} else if (RichTextParser.startsWithNIP19Scheme(myUrlPreview)) {
|
||||
val bgColor = MaterialTheme.colorScheme.background
|
||||
val backgroundColor = remember { mutableStateOf(bgColor) }
|
||||
when {
|
||||
RichTextParser.isValidURL(myUrlPreview) -> {
|
||||
when {
|
||||
RichTextParser.isImageUrl(myUrlPreview) -> {
|
||||
AsyncImage(
|
||||
model = myUrlPreview,
|
||||
contentDescription = myUrlPreview,
|
||||
contentScale = ContentScale.FillHeight,
|
||||
modifier = Modifier.fillMaxHeight().aspectRatio(1f),
|
||||
)
|
||||
}
|
||||
|
||||
BechLinkPreview(
|
||||
word = myUrlPreview,
|
||||
canPreview = true,
|
||||
quotesLeft = 1,
|
||||
backgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
} else if (RichTextParser.isUrlWithoutScheme(myUrlPreview)) {
|
||||
MyLoadUrlPreviewDirect("https://$myUrlPreview", myUrlPreview, accountViewModel)
|
||||
RichTextParser.isVideoUrl(myUrlPreview) -> {
|
||||
VideoView(
|
||||
myUrlPreview,
|
||||
mimeType = null,
|
||||
roundedCorner = false,
|
||||
gallery = false,
|
||||
contentScale = ContentScale.FillHeight,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
MyLoadUrlPreviewDirect(myUrlPreview, myUrlPreview, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RichTextParser.startsWithNIP19Scheme(myUrlPreview) -> {
|
||||
val bgColor = MaterialTheme.colorScheme.background
|
||||
val backgroundColor = remember { mutableStateOf(bgColor) }
|
||||
|
||||
BechLinkPreview(
|
||||
word = myUrlPreview,
|
||||
canPreview = true,
|
||||
quotesLeft = 1,
|
||||
backgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
RichTextParser.isUrlWithoutScheme(myUrlPreview) -> {
|
||||
MyLoadUrlPreviewDirect("https://$myUrlPreview", myUrlPreview, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,24 +116,30 @@ fun PreviewUrlFillWidth(
|
||||
nav: INav,
|
||||
) {
|
||||
if (RichTextParser.isValidURL(myUrlPreview)) {
|
||||
if (RichTextParser.isImageUrl(myUrlPreview)) {
|
||||
AsyncImage(
|
||||
model = myUrlPreview,
|
||||
contentDescription = myUrlPreview,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier.fillMaxHeight().aspectRatio(1f),
|
||||
)
|
||||
} else if (RichTextParser.isVideoUrl(myUrlPreview)) {
|
||||
VideoView(
|
||||
myUrlPreview,
|
||||
mimeType = null,
|
||||
roundedCorner = false,
|
||||
gallery = false,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
} else {
|
||||
MyLoadUrlPreviewDirectFillWidth(myUrlPreview, myUrlPreview, accountViewModel)
|
||||
when {
|
||||
RichTextParser.isImageUrl(myUrlPreview) -> {
|
||||
AsyncImage(
|
||||
model = myUrlPreview,
|
||||
contentDescription = myUrlPreview,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier.fillMaxHeight().aspectRatio(1f),
|
||||
)
|
||||
}
|
||||
|
||||
RichTextParser.isVideoUrl(myUrlPreview) -> {
|
||||
VideoView(
|
||||
myUrlPreview,
|
||||
mimeType = null,
|
||||
roundedCorner = false,
|
||||
gallery = false,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
accountViewModel = accountViewModel,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
MyLoadUrlPreviewDirectFillWidth(myUrlPreview, myUrlPreview, accountViewModel)
|
||||
}
|
||||
}
|
||||
} else if (RichTextParser.startsWithNIP19Scheme(myUrlPreview)) {
|
||||
val bgColor = MaterialTheme.colorScheme.background
|
||||
|
||||
+47
-39
@@ -92,50 +92,58 @@ class UserSuggestionState(
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (nip05 != null) {
|
||||
runCatching {
|
||||
nip05Client.get(nip05)?.let { info ->
|
||||
val user = account.cache.checkGetOrCreateUser(info.pubkey)
|
||||
if (user != null) {
|
||||
info.relays.forEach {
|
||||
it.normalizeRelayUrlOrNull()?.let { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
when {
|
||||
nip05 != null -> {
|
||||
runCatching {
|
||||
nip05Client.get(nip05)?.let { info ->
|
||||
val user = account.cache.checkGetOrCreateUser(info.pubkey)
|
||||
if (user != null) {
|
||||
info.relays.forEach {
|
||||
it.normalizeRelayUrlOrNull()?.let { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
}
|
||||
}
|
||||
}
|
||||
user
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
prefix.startsWithAny(userUriPrefixes) -> {
|
||||
runCatching {
|
||||
Nip19Parser.uriToRoute(prefix)?.entity?.let { parsed ->
|
||||
when (parsed) {
|
||||
is NSec -> {
|
||||
account.cache.getOrCreateUser(parsed.toPubKey().toHexKey())
|
||||
}
|
||||
|
||||
is NPub -> {
|
||||
account.cache.getOrCreateUser(parsed.hex)
|
||||
}
|
||||
|
||||
is NProfile -> {
|
||||
val user = account.cache.getOrCreateUser(parsed.hex)
|
||||
parsed.relay.forEach { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
}
|
||||
user
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
user
|
||||
}
|
||||
}.getOrNull()
|
||||
} else if (prefix.startsWithAny(userUriPrefixes)) {
|
||||
runCatching {
|
||||
Nip19Parser.uriToRoute(prefix)?.entity?.let { parsed ->
|
||||
when (parsed) {
|
||||
is NSec -> {
|
||||
account.cache.getOrCreateUser(parsed.toPubKey().toHexKey())
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
is NPub -> {
|
||||
account.cache.getOrCreateUser(parsed.hex)
|
||||
}
|
||||
prefix.length == 64 && Hex.isHex64(prefix) -> {
|
||||
account.cache.getOrCreateUser(prefix)
|
||||
}
|
||||
|
||||
is NProfile -> {
|
||||
val user = account.cache.getOrCreateUser(parsed.hex)
|
||||
parsed.relay.forEach { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
}
|
||||
user
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}.getOrNull()
|
||||
} else if (prefix.length == 64 && Hex.isHex64(prefix)) {
|
||||
account.cache.getOrCreateUser(prefix)
|
||||
} else {
|
||||
null
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
|
||||
@@ -331,60 +331,66 @@ fun RenderAttestationRequest(
|
||||
}
|
||||
|
||||
if (quotesLeft > 0) {
|
||||
if (aboutAddress != null) {
|
||||
LoadAddressableNote(aboutAddress, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = stringRes(R.string.attestation_requests_attestation_to),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
NoteCompose(
|
||||
baseNote = it,
|
||||
modifier = MaterialTheme.colorScheme.replyModifier,
|
||||
isQuotedNote = true,
|
||||
unPackReply = ReplyRenderType.NONE,
|
||||
makeItShort = true,
|
||||
quotesLeft = quotesLeft - 1,
|
||||
parentBackgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
when {
|
||||
aboutAddress != null -> {
|
||||
LoadAddressableNote(aboutAddress, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = stringRes(R.string.attestation_requests_attestation_to),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
NoteCompose(
|
||||
baseNote = it,
|
||||
modifier = MaterialTheme.colorScheme.replyModifier,
|
||||
isQuotedNote = true,
|
||||
unPackReply = ReplyRenderType.NONE,
|
||||
makeItShort = true,
|
||||
quotesLeft = quotesLeft - 1,
|
||||
parentBackgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (aboutEvent != null) {
|
||||
LoadNote(aboutEvent, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = stringRes(R.string.attestation_requests_attestation_to),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
NoteCompose(
|
||||
baseNote = it,
|
||||
modifier = MaterialTheme.colorScheme.replyModifier,
|
||||
isQuotedNote = true,
|
||||
unPackReply = ReplyRenderType.NONE,
|
||||
makeItShort = true,
|
||||
quotesLeft = quotesLeft - 1,
|
||||
parentBackgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
aboutEvent != null -> {
|
||||
LoadNote(aboutEvent, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = stringRes(R.string.attestation_requests_attestation_to),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
NoteCompose(
|
||||
baseNote = it,
|
||||
modifier = MaterialTheme.colorScheme.replyModifier,
|
||||
isQuotedNote = true,
|
||||
unPackReply = ReplyRenderType.NONE,
|
||||
makeItShort = true,
|
||||
quotesLeft = quotesLeft - 1,
|
||||
parentBackgroundColor = backgroundColor,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (aboutPubkey != null) {
|
||||
LoadUser(aboutPubkey, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = stringRes(R.string.attestation_requests_attestation_to),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
UserCompose(it, accountViewModel = accountViewModel, nav = nav)
|
||||
|
||||
aboutPubkey != null -> {
|
||||
LoadUser(aboutPubkey, accountViewModel) {
|
||||
if (it != null) {
|
||||
Spacer(modifier = DoubleVertSpacer)
|
||||
Text(
|
||||
text = stringRes(R.string.attestation_requests_attestation_to),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
UserCompose(it, accountViewModel = accountViewModel, nav = nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,21 +278,29 @@ private fun DisplayQuoteAuthor(
|
||||
}
|
||||
}
|
||||
|
||||
if (addressable != null) {
|
||||
addressable?.let {
|
||||
DisplayEntryForNote(it, userBase, accountViewModel, nav)
|
||||
when {
|
||||
addressable != null -> {
|
||||
addressable?.let {
|
||||
DisplayEntryForNote(it, userBase, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
} else if (version != null) {
|
||||
version?.let {
|
||||
DisplayEntryForNote(it, userBase, accountViewModel, nav)
|
||||
}
|
||||
} else if (baseUrl != null) {
|
||||
val url = "$baseUrl${if (baseUrl.contains("#")) "&" else "#"}:~:text=${Uri.encode(highlightQuote)}"
|
||||
|
||||
DisplayEntryForAUrl(url, userBase, accountViewModel, nav)
|
||||
} else if (userBase != null) {
|
||||
userBase?.let {
|
||||
DisplayEntryForUser(it, accountViewModel, nav)
|
||||
version != null -> {
|
||||
version?.let {
|
||||
DisplayEntryForNote(it, userBase, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
baseUrl != null -> {
|
||||
val url = "$baseUrl${if (baseUrl.contains("#")) "&" else "#"}:~:text=${Uri.encode(highlightQuote)}"
|
||||
|
||||
DisplayEntryForAUrl(url, userBase, accountViewModel, nav)
|
||||
}
|
||||
|
||||
userBase != null -> {
|
||||
userBase?.let {
|
||||
DisplayEntryForUser(it, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,14 +233,11 @@ fun RenderEyeGlassesPrescription(
|
||||
val isContacts = contactsRightEye != null || contactsLeftEye != null
|
||||
|
||||
Text(
|
||||
if (isGlasses && isContacts) {
|
||||
"Vision Prescription"
|
||||
} else if (isGlasses) {
|
||||
"Glasses Prescription"
|
||||
} else if (isContacts) {
|
||||
"Contact Lenses Prescription"
|
||||
} else {
|
||||
"Empty Prescription"
|
||||
when {
|
||||
isGlasses && isContacts -> "Vision Prescription"
|
||||
isGlasses -> "Glasses Prescription"
|
||||
isContacts -> "Contact Lenses Prescription"
|
||||
else -> "Empty Prescription"
|
||||
},
|
||||
modifier = Modifier.padding(4.dp).fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
|
||||
+39
-29
@@ -134,35 +134,45 @@ class AccountSessionManager(
|
||||
}
|
||||
|
||||
val accountSettings =
|
||||
if (loginWithExternalSigner) {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(pubKey = pubKeyParsed),
|
||||
transientAccount = transientAccount,
|
||||
externalSignerPackageName = packageName.ifBlank { "com.greenart7c3.nostrsigner" },
|
||||
)
|
||||
} else if (key.startsWith("nsec")) {
|
||||
val privHex =
|
||||
decodePrivateKeyAsHexOrNull(key)
|
||||
?: throw Exception("Invalid nsec key")
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(privKey = privHex.hexToByteArray()),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
} else if (key.contains(" ") && Nip06().isValidMnemonic(key)) {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(privKey = Nip06().privateKeyFromMnemonic(key)),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
} else if (pubKeyParsed != null) {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(pubKey = pubKeyParsed),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
} else {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(Hex.decode(key)),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
when {
|
||||
loginWithExternalSigner -> {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(pubKey = pubKeyParsed),
|
||||
transientAccount = transientAccount,
|
||||
externalSignerPackageName = packageName.ifBlank { "com.greenart7c3.nostrsigner" },
|
||||
)
|
||||
}
|
||||
|
||||
key.startsWith("nsec") -> {
|
||||
val privHex =
|
||||
decodePrivateKeyAsHexOrNull(key)
|
||||
?: throw Exception("Invalid nsec key")
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(privKey = privHex.hexToByteArray()),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
}
|
||||
|
||||
key.contains(" ") && Nip06().isValidMnemonic(key) -> {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(privKey = Nip06().privateKeyFromMnemonic(key)),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
}
|
||||
|
||||
pubKeyParsed != null -> {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(pubKey = pubKeyParsed),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(Hex.decode(key)),
|
||||
transientAccount = transientAccount,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
localPreferences.setDefaultAccount(accountSettings)
|
||||
|
||||
+16
-10
@@ -1404,16 +1404,22 @@ class AccountViewModel(
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
for (note in notes) {
|
||||
val noteEvent = note.event
|
||||
if (noteEvent is IsInPublicChatChannel) {
|
||||
account.markAsRead("Channel/${noteEvent.channelId()}", noteEvent.createdAt)
|
||||
} else if (noteEvent is ChatroomKeyable) {
|
||||
account.markAsRead("Room/${noteEvent.chatroomKey(account.signer.pubKey).hashCode()}", noteEvent.createdAt)
|
||||
} else if (noteEvent is DraftWrapEvent) {
|
||||
val innerEvent = account.draftsDecryptionCache.preCachedDraft(noteEvent)
|
||||
if (innerEvent is IsInPublicChatChannel) {
|
||||
account.markAsRead("Channel/${innerEvent.channelId()}", noteEvent.createdAt)
|
||||
} else if (innerEvent is ChatroomKeyable) {
|
||||
account.markAsRead("Room/${innerEvent.chatroomKey(account.signer.pubKey).hashCode()}", noteEvent.createdAt)
|
||||
when {
|
||||
noteEvent is IsInPublicChatChannel -> {
|
||||
account.markAsRead("Channel/${noteEvent.channelId()}", noteEvent.createdAt)
|
||||
}
|
||||
|
||||
noteEvent is ChatroomKeyable -> {
|
||||
account.markAsRead("Room/${noteEvent.chatroomKey(account.signer.pubKey).hashCode()}", noteEvent.createdAt)
|
||||
}
|
||||
|
||||
noteEvent is DraftWrapEvent -> {
|
||||
val innerEvent = account.draftsDecryptionCache.preCachedDraft(noteEvent)
|
||||
if (innerEvent is IsInPublicChatChannel) {
|
||||
account.markAsRead("Channel/${innerEvent.channelId()}", noteEvent.createdAt)
|
||||
} else if (innerEvent is ChatroomKeyable) {
|
||||
account.markAsRead("Room/${innerEvent.chatroomKey(account.signer.pubKey).hashCode()}", noteEvent.createdAt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -172,15 +172,17 @@ fun NormalChatNote(
|
||||
remember(note) {
|
||||
derivedStateOf {
|
||||
val noteEvent = note.event
|
||||
if (accountViewModel.isLoggedUser(note.author)) {
|
||||
false // never shows the user's pictures
|
||||
} else if (noteEvent is PrivateDmEvent) {
|
||||
false // one-on-one, never shows it.
|
||||
} else if (noteEvent is ChatroomKeyable) {
|
||||
when {
|
||||
accountViewModel.isLoggedUser(note.author) -> false
|
||||
|
||||
// never shows the user's pictures
|
||||
noteEvent is PrivateDmEvent -> false
|
||||
|
||||
// one-on-one, never shows it.
|
||||
// only shows in a group chat.
|
||||
noteEvent.chatroomKey(accountViewModel.userProfile().pubkeyHex).users.size > 1
|
||||
} else {
|
||||
true
|
||||
noteEvent is ChatroomKeyable -> noteEvent.chatroomKey(accountViewModel.userProfile().pubkeyHex).users.size > 1
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-11
@@ -735,17 +735,25 @@ class ChatNewMessageViewModel :
|
||||
|
||||
fun autocompleteWithUser(item: User) {
|
||||
userSuggestions?.let { userSuggestions ->
|
||||
if (userSuggestionsMainMessage == UserSuggestionAnchor.MAIN_MESSAGE) {
|
||||
val lastWord = message.currentWord()
|
||||
userSuggestions.replaceCurrentWord(message, lastWord, item)
|
||||
urlPreviews.update(message.text.toString())
|
||||
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.FORWARD_ZAPS) {
|
||||
forwardZapTo.value.addItem(item)
|
||||
forwardZapToEditting.value = TextFieldValue("")
|
||||
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.TO_USERS) {
|
||||
val lastWord = toUsers.currentWord()
|
||||
userSuggestions.replaceCurrentWord(toUsers, lastWord, item)
|
||||
updateRoomFromUsersInput()
|
||||
when (userSuggestionsMainMessage) {
|
||||
UserSuggestionAnchor.MAIN_MESSAGE -> {
|
||||
val lastWord = message.currentWord()
|
||||
userSuggestions.replaceCurrentWord(message, lastWord, item)
|
||||
urlPreviews.update(message.text.toString())
|
||||
}
|
||||
|
||||
UserSuggestionAnchor.FORWARD_ZAPS -> {
|
||||
forwardZapTo.value.addItem(item)
|
||||
forwardZapToEditting.value = TextFieldValue("")
|
||||
}
|
||||
|
||||
UserSuggestionAnchor.TO_USERS -> {
|
||||
val lastWord = toUsers.currentWord()
|
||||
userSuggestions.replaceCurrentWord(toUsers, lastWord, item)
|
||||
updateRoomFromUsersInput()
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
userSuggestionsMainMessage = null
|
||||
|
||||
+87
-79
@@ -415,85 +415,106 @@ open class ChannelNewMessageViewModel :
|
||||
val contentWarningReason = if (wantsToMarkAsSensitive) contentWarningDescription else null
|
||||
val localExpirationDate = if (wantsExpirationDate) expirationDate else null
|
||||
|
||||
return if (channel is PublicChatChannel) {
|
||||
val replyingToEvent = replyTo.value?.toEventHint<ChannelMessageEvent>()
|
||||
val channelEvent = channel.event
|
||||
return when {
|
||||
channel is PublicChatChannel -> {
|
||||
val replyingToEvent = replyTo.value?.toEventHint<ChannelMessageEvent>()
|
||||
val channelEvent = channel.event
|
||||
|
||||
if (replyingToEvent != null) {
|
||||
ChannelMessageEvent.reply(tagger.message, replyingToEvent) {
|
||||
notify(replyingToEvent.toPTag())
|
||||
if (replyingToEvent != null) {
|
||||
ChannelMessageEvent.reply(tagger.message, replyingToEvent) {
|
||||
notify(replyingToEvent.toPTag())
|
||||
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
geoHash?.let { geohash(it) }
|
||||
geoHash?.let { geohash(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
} else if (channelEvent != null) {
|
||||
val hint = EventHintBundle(channelEvent, channelRelays.firstOrNull())
|
||||
ChannelMessageEvent.message(tagger.message, hint) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
} else if (channelEvent != null) {
|
||||
val hint = EventHintBundle(channelEvent, channelRelays.firstOrNull())
|
||||
ChannelMessageEvent.message(tagger.message, hint) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
geoHash?.let { geohash(it) }
|
||||
geoHash?.let { geohash(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
} else {
|
||||
ChannelMessageEvent.message(tagger.message, ETag(channel.idHex, channelRelays.firstOrNull())) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
} else {
|
||||
ChannelMessageEvent.message(tagger.message, ETag(channel.idHex, channelRelays.firstOrNull())) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
geoHash?.let { geohash(it) }
|
||||
geoHash?.let { geohash(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (channel is LiveActivitiesChannel) {
|
||||
val replyingToEvent = replyTo.value?.toEventHint<LiveActivitiesChatMessageEvent>()
|
||||
val activity = channel.info
|
||||
|
||||
if (replyingToEvent != null) {
|
||||
LiveActivitiesChatMessageEvent.reply(tagger.message, replyingToEvent) {
|
||||
notify(replyingToEvent.toPTag())
|
||||
channel is LiveActivitiesChannel -> {
|
||||
val replyingToEvent = replyTo.value?.toEventHint<LiveActivitiesChatMessageEvent>()
|
||||
val activity = channel.info
|
||||
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
if (replyingToEvent != null) {
|
||||
LiveActivitiesChatMessageEvent.reply(tagger.message, replyingToEvent) {
|
||||
notify(replyingToEvent.toPTag())
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
} else if (activity != null) {
|
||||
val hint = EventHintBundle(activity, channelRelays.firstOrNull())
|
||||
|
||||
LiveActivitiesChatMessageEvent.message(tagger.message, hint) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
} else {
|
||||
LiveActivitiesChatMessageEvent.message(tagger.message, channel.toATag()) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
}
|
||||
} else if (activity != null) {
|
||||
val hint = EventHintBundle(activity, channelRelays.firstOrNull())
|
||||
}
|
||||
|
||||
LiveActivitiesChatMessageEvent.message(tagger.message, hint) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
} else {
|
||||
LiveActivitiesChatMessageEvent.message(tagger.message, channel.toATag()) {
|
||||
channel is EphemeralChatChannel -> {
|
||||
EphemeralChatEvent.build(
|
||||
tagger.message,
|
||||
channel.roomId.relayUrl,
|
||||
channel.roomId.id,
|
||||
) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
@@ -504,23 +525,10 @@ open class ChannelNewMessageViewModel :
|
||||
imetas(usedAttachments)
|
||||
}
|
||||
}
|
||||
} else if (channel is EphemeralChatChannel) {
|
||||
EphemeralChatEvent.build(
|
||||
tagger.message,
|
||||
channel.roomId.relayUrl,
|
||||
channel.roomId.id,
|
||||
) {
|
||||
hashtags(findHashtags(tagger.message))
|
||||
references(findURLs(tagger.message))
|
||||
quotes(findNostrUris(tagger.message))
|
||||
contentWarningReason?.let { contentWarning(it) }
|
||||
localExpirationDate?.let { expiration(it) }
|
||||
|
||||
emojis(emojis)
|
||||
imetas(usedAttachments)
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-12
@@ -140,18 +140,26 @@ fun RenderContentDVMThumb(
|
||||
card.amount?.let {
|
||||
var color = Color.DarkGray
|
||||
var amount = it
|
||||
if (card.amount == "free" || card.amount == "0") {
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
amount = "Free"
|
||||
} else if (card.amount == "flexible") {
|
||||
color = MaterialTheme.colorScheme.primaryContainer
|
||||
amount = "Flexible"
|
||||
} else if (card.amount == "") {
|
||||
color = MaterialTheme.colorScheme.grayText
|
||||
amount = "Unknown"
|
||||
} else {
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
amount = card.amount + " Sats"
|
||||
when {
|
||||
card.amount == "free" || card.amount == "0" -> {
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
amount = "Free"
|
||||
}
|
||||
|
||||
card.amount == "flexible" -> {
|
||||
color = MaterialTheme.colorScheme.primaryContainer
|
||||
amount = "Flexible"
|
||||
}
|
||||
|
||||
card.amount == "" -> {
|
||||
color = MaterialTheme.colorScheme.grayText
|
||||
amount = "Unknown"
|
||||
}
|
||||
|
||||
else -> {
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
amount = card.amount + " Sats"
|
||||
}
|
||||
}
|
||||
Text(
|
||||
textAlign = TextAlign.End,
|
||||
|
||||
+84
-70
@@ -98,41 +98,48 @@ class NotificationSummaryState(
|
||||
LocalCache.notes.forEach { _, it ->
|
||||
val noteEvent = it.event
|
||||
if (noteEvent != null && !takenIntoAccount.contains(noteEvent.id)) {
|
||||
if (noteEvent is ReactionEvent) {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
reactions[netDate] = (reactions[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
}
|
||||
} else if (noteEvent is RepostEvent || noteEvent is GenericRepostEvent) {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
}
|
||||
} else if (noteEvent is LnZapEvent) {
|
||||
// the user might be sending his own receipts noteEvent.pubKey != currentUser
|
||||
if (noteEvent.isTaggedUser(currentUser)) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
zaps[netDate] = (zaps[netDate] ?: BigDecimal.ZERO) + (noteEvent.amount ?: BigDecimal.ZERO)
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
}
|
||||
} else if (noteEvent is BaseThreadedEvent &&
|
||||
noteEvent.isTaggedUser(currentUser) &&
|
||||
noteEvent.pubKey != currentUser
|
||||
) {
|
||||
val isCitation =
|
||||
noteEvent.findCitations().any {
|
||||
LocalCache.getNoteIfExists(it)?.author?.pubkeyHex == currentUser
|
||||
when {
|
||||
noteEvent is ReactionEvent -> {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
reactions[netDate] = (reactions[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
}
|
||||
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
if (isCitation) {
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
} else {
|
||||
replies[netDate] = (replies[netDate] ?: 0) + 1
|
||||
}
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
|
||||
noteEvent is RepostEvent || noteEvent is GenericRepostEvent -> {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
}
|
||||
}
|
||||
|
||||
noteEvent is LnZapEvent -> {
|
||||
// the user might be sending his own receipts noteEvent.pubKey != currentUser
|
||||
if (noteEvent.isTaggedUser(currentUser)) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
zaps[netDate] = (zaps[netDate] ?: BigDecimal.ZERO) + (noteEvent.amount ?: BigDecimal.ZERO)
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
}
|
||||
}
|
||||
|
||||
noteEvent is BaseThreadedEvent &&
|
||||
noteEvent.isTaggedUser(currentUser) &&
|
||||
noteEvent.pubKey != currentUser -> {
|
||||
val isCitation =
|
||||
noteEvent.findCitations().any {
|
||||
LocalCache.getNoteIfExists(it)?.author?.pubkeyHex == currentUser
|
||||
}
|
||||
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
if (isCitation) {
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
} else {
|
||||
replies[netDate] = (replies[netDate] ?: 0) + 1
|
||||
}
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,45 +169,52 @@ class NotificationSummaryState(
|
||||
newNotes.forEach {
|
||||
val noteEvent = it.event
|
||||
if (noteEvent != null && !takenIntoAccount.contains(noteEvent.id)) {
|
||||
if (noteEvent is ReactionEvent) {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
reactions[netDate] = (reactions[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
}
|
||||
} else if (noteEvent is RepostEvent || noteEvent is GenericRepostEvent) {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
}
|
||||
} else if (noteEvent is LnZapEvent) {
|
||||
if (noteEvent.isTaggedUser(currentUser)) {
|
||||
// && noteEvent.pubKey != currentUser User might be sending his own receipts
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
zaps[netDate] = (zaps[netDate] ?: BigDecimal.ZERO) + (noteEvent.amount ?: BigDecimal.ZERO)
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
}
|
||||
} else if (noteEvent is BaseThreadedEvent &&
|
||||
noteEvent.isTaggedUser(currentUser) &&
|
||||
noteEvent.pubKey != currentUser
|
||||
) {
|
||||
val isCitation =
|
||||
noteEvent.findCitations().any {
|
||||
LocalCache.getNoteIfExists(it)?.author?.pubkeyHex == currentUser
|
||||
when {
|
||||
noteEvent is ReactionEvent -> {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
reactions[netDate] = (reactions[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
}
|
||||
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
if (isCitation) {
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
} else {
|
||||
replies[netDate] = (replies[netDate] ?: 0) + 1
|
||||
}
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
|
||||
noteEvent is RepostEvent || noteEvent is GenericRepostEvent -> {
|
||||
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
}
|
||||
}
|
||||
|
||||
noteEvent is LnZapEvent -> {
|
||||
if (noteEvent.isTaggedUser(currentUser)) {
|
||||
// && noteEvent.pubKey != currentUser User might be sending his own receipts
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
zaps[netDate] = (zaps[netDate] ?: BigDecimal.ZERO) + (noteEvent.amount ?: BigDecimal.ZERO)
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
}
|
||||
}
|
||||
|
||||
noteEvent is BaseThreadedEvent &&
|
||||
noteEvent.isTaggedUser(currentUser) &&
|
||||
noteEvent.pubKey != currentUser -> {
|
||||
val isCitation =
|
||||
noteEvent.findCitations().any {
|
||||
LocalCache.getNoteIfExists(it)?.author?.pubkeyHex == currentUser
|
||||
}
|
||||
|
||||
val netDate = formatDate(noteEvent.createdAt)
|
||||
if (isCitation) {
|
||||
boosts[netDate] = (boosts[netDate] ?: 0) + 1
|
||||
} else {
|
||||
replies[netDate] = (replies[netDate] ?: 0) + 1
|
||||
}
|
||||
takenIntoAccount.add(noteEvent.id)
|
||||
hasNewElements = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-10
@@ -598,16 +598,24 @@ class NewPublicMessageViewModel :
|
||||
|
||||
fun autocompleteWithUser(item: User) {
|
||||
userSuggestions?.let { userSuggestions ->
|
||||
if (userSuggestionsMainMessage == UserSuggestionAnchor.MAIN_MESSAGE) {
|
||||
val lastWord = message.currentWord()
|
||||
userSuggestions.replaceCurrentWord(message, lastWord, item)
|
||||
urlPreviews.update(message.text.toString())
|
||||
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.FORWARD_ZAPS) {
|
||||
forwardZapTo.value.addItem(item)
|
||||
forwardZapToEditting.value = TextFieldValue("")
|
||||
} else if (userSuggestionsMainMessage == UserSuggestionAnchor.TO_USERS) {
|
||||
val lastWord = toUsers.currentWord()
|
||||
userSuggestions.replaceCurrentWord(toUsers, lastWord, item)
|
||||
when (userSuggestionsMainMessage) {
|
||||
UserSuggestionAnchor.MAIN_MESSAGE -> {
|
||||
val lastWord = message.currentWord()
|
||||
userSuggestions.replaceCurrentWord(message, lastWord, item)
|
||||
urlPreviews.update(message.text.toString())
|
||||
}
|
||||
|
||||
UserSuggestionAnchor.FORWARD_ZAPS -> {
|
||||
forwardZapTo.value.addItem(item)
|
||||
forwardZapToEditting.value = TextFieldValue("")
|
||||
}
|
||||
|
||||
UserSuggestionAnchor.TO_USERS -> {
|
||||
val lastWord = toUsers.currentWord()
|
||||
userSuggestions.replaceCurrentWord(toUsers, lastWord, item)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
userSuggestionsMainMessage = null
|
||||
|
||||
+93
-83
@@ -79,100 +79,110 @@ fun GalleryThumbnail(
|
||||
val noteEvent = noteState.note.event ?: return
|
||||
|
||||
val content =
|
||||
if (noteEvent is ProfileGalleryEntryEvent) {
|
||||
noteEvent.urls().map { url ->
|
||||
if (isVideoUrl(url)) {
|
||||
MediaUrlVideo(
|
||||
url = url,
|
||||
description = noteEvent.content,
|
||||
hash = null,
|
||||
blurhash = noteEvent.blurhash(),
|
||||
dim = noteEvent.dimensions(),
|
||||
uri = null,
|
||||
mimeType = noteEvent.mimeType(),
|
||||
thumbhash = noteEvent.thumbhash(),
|
||||
artworkUri = noteEvent.image()?.imageUrl ?: noteEvent.thumb()?.imageUrl,
|
||||
)
|
||||
} else {
|
||||
when {
|
||||
noteEvent is ProfileGalleryEntryEvent -> {
|
||||
noteEvent.urls().map { url ->
|
||||
if (isVideoUrl(url)) {
|
||||
MediaUrlVideo(
|
||||
url = url,
|
||||
description = noteEvent.content,
|
||||
hash = null,
|
||||
blurhash = noteEvent.blurhash(),
|
||||
dim = noteEvent.dimensions(),
|
||||
uri = null,
|
||||
mimeType = noteEvent.mimeType(),
|
||||
thumbhash = noteEvent.thumbhash(),
|
||||
artworkUri = noteEvent.image()?.imageUrl ?: noteEvent.thumb()?.imageUrl,
|
||||
)
|
||||
} else {
|
||||
MediaUrlImage(
|
||||
url = url,
|
||||
description = noteEvent.content,
|
||||
// We don't want to show the hash banner here
|
||||
hash = null,
|
||||
blurhash = noteEvent.blurhash(),
|
||||
dim = noteEvent.dimensions(),
|
||||
uri = null,
|
||||
mimeType = noteEvent.mimeType(),
|
||||
thumbhash = noteEvent.thumbhash(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
noteEvent is PictureEvent -> {
|
||||
noteEvent.imetaTags().map { imeta ->
|
||||
MediaUrlImage(
|
||||
url = url,
|
||||
url = imeta.url,
|
||||
description = noteEvent.content,
|
||||
// We don't want to show the hash banner here
|
||||
hash = null,
|
||||
blurhash = noteEvent.blurhash(),
|
||||
dim = noteEvent.dimensions(),
|
||||
blurhash = imeta.blurhash,
|
||||
dim = imeta.dimension,
|
||||
uri = null,
|
||||
mimeType = noteEvent.mimeType(),
|
||||
thumbhash = noteEvent.thumbhash(),
|
||||
mimeType = imeta.mimeType,
|
||||
thumbhash = imeta.thumbhash,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (noteEvent is PictureEvent) {
|
||||
noteEvent.imetaTags().map { imeta ->
|
||||
MediaUrlImage(
|
||||
url = imeta.url,
|
||||
description = noteEvent.content,
|
||||
// We don't want to show the hash banner here
|
||||
hash = null,
|
||||
blurhash = imeta.blurhash,
|
||||
dim = imeta.dimension,
|
||||
uri = null,
|
||||
mimeType = imeta.mimeType,
|
||||
thumbhash = imeta.thumbhash,
|
||||
)
|
||||
}
|
||||
} else if (noteEvent is VideoEvent) {
|
||||
// An HLS publish writes one imeta per rendition (master + each variant) on the same
|
||||
// NIP-71 event. Rendering them all expands a single video into a sub-grid of black
|
||||
// tiles inside one gallery card — the .m3u8 playlist is a text manifest Coil can't
|
||||
// decode. Collapse to a single tile when every imeta is an HLS playlist; prefer an
|
||||
// imeta that carries a poster image, breaking ties by smallest dimensions so we
|
||||
// pick the lowest-resolution variant. Non-HLS multi-imeta videos keep the existing
|
||||
// per-imeta layout.
|
||||
val imetas = noteEvent.imetaTags()
|
||||
val isAllHls = imetas.isNotEmpty() && imetas.all { isHlsMimeType(it.mimeType) }
|
||||
val toRender =
|
||||
if (isAllHls) {
|
||||
val withPoster = imetas.filter { it.image.isNotEmpty() }
|
||||
val pick =
|
||||
withPoster.ifEmpty { imetas }.minBy {
|
||||
it.dimension?.let { d -> d.width * d.height } ?: Int.MAX_VALUE
|
||||
}
|
||||
listOf(pick)
|
||||
} else {
|
||||
imetas
|
||||
}
|
||||
toRender.map { imeta ->
|
||||
MediaUrlVideo(
|
||||
url = imeta.url,
|
||||
description = noteEvent.content,
|
||||
// We don't want to show the hash banner here
|
||||
hash = null,
|
||||
blurhash = imeta.blurhash,
|
||||
dim = imeta.dimension,
|
||||
uri = null,
|
||||
mimeType = imeta.mimeType,
|
||||
thumbhash = imeta.thumbhash,
|
||||
artworkUri = imeta.image.firstOrNull(),
|
||||
)
|
||||
}
|
||||
} else if (noteEvent is LiveActivitiesClipEvent) {
|
||||
noteEvent.videoUrl()?.let { url ->
|
||||
listOf(
|
||||
|
||||
noteEvent is VideoEvent -> {
|
||||
// An HLS publish writes one imeta per rendition (master + each variant) on the same
|
||||
// NIP-71 event. Rendering them all expands a single video into a sub-grid of black
|
||||
// tiles inside one gallery card — the .m3u8 playlist is a text manifest Coil can't
|
||||
// decode. Collapse to a single tile when every imeta is an HLS playlist; prefer an
|
||||
// imeta that carries a poster image, breaking ties by smallest dimensions so we
|
||||
// pick the lowest-resolution variant. Non-HLS multi-imeta videos keep the existing
|
||||
// per-imeta layout.
|
||||
val imetas = noteEvent.imetaTags()
|
||||
val isAllHls = imetas.isNotEmpty() && imetas.all { isHlsMimeType(it.mimeType) }
|
||||
val toRender =
|
||||
if (isAllHls) {
|
||||
val withPoster = imetas.filter { it.image.isNotEmpty() }
|
||||
val pick =
|
||||
withPoster.ifEmpty { imetas }.minBy {
|
||||
it.dimension?.let { d -> d.width * d.height } ?: Int.MAX_VALUE
|
||||
}
|
||||
listOf(pick)
|
||||
} else {
|
||||
imetas
|
||||
}
|
||||
toRender.map { imeta ->
|
||||
MediaUrlVideo(
|
||||
url = url,
|
||||
description = noteEvent.title() ?: noteEvent.content,
|
||||
url = imeta.url,
|
||||
description = noteEvent.content,
|
||||
// We don't want to show the hash banner here
|
||||
hash = null,
|
||||
blurhash = null,
|
||||
dim = null,
|
||||
blurhash = imeta.blurhash,
|
||||
dim = imeta.dimension,
|
||||
uri = null,
|
||||
mimeType = null,
|
||||
thumbhash = null,
|
||||
),
|
||||
)
|
||||
} ?: emptyList()
|
||||
} else {
|
||||
emptyList()
|
||||
mimeType = imeta.mimeType,
|
||||
thumbhash = imeta.thumbhash,
|
||||
artworkUri = imeta.image.firstOrNull(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
noteEvent is LiveActivitiesClipEvent -> {
|
||||
noteEvent.videoUrl()?.let { url ->
|
||||
listOf(
|
||||
MediaUrlVideo(
|
||||
url = url,
|
||||
description = noteEvent.title() ?: noteEvent.content,
|
||||
hash = null,
|
||||
blurhash = null,
|
||||
dim = null,
|
||||
uri = null,
|
||||
mimeType = null,
|
||||
thumbhash = null,
|
||||
),
|
||||
)
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
else -> {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
InnerRenderGalleryThumb(content, baseNote, accountViewModel)
|
||||
|
||||
+47
-39
@@ -125,50 +125,58 @@ class SearchBarViewModel(
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (nip05 != null) {
|
||||
runCatching {
|
||||
nip05Client.get(nip05)?.let { info ->
|
||||
val user = account.cache.checkGetOrCreateUser(info.pubkey)
|
||||
if (user != null) {
|
||||
info.relays.forEach {
|
||||
it.normalizeRelayUrlOrNull()?.let { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
when {
|
||||
nip05 != null -> {
|
||||
runCatching {
|
||||
nip05Client.get(nip05)?.let { info ->
|
||||
val user = account.cache.checkGetOrCreateUser(info.pubkey)
|
||||
if (user != null) {
|
||||
info.relays.forEach {
|
||||
it.normalizeRelayUrlOrNull()?.let { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
}
|
||||
}
|
||||
}
|
||||
user
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
term.startsWithAny(userUriPrefixes) -> {
|
||||
runCatching {
|
||||
Nip19Parser.uriToRoute(term)?.entity?.let { parsed ->
|
||||
when (parsed) {
|
||||
is NSec -> {
|
||||
account.cache.getOrCreateUser(parsed.toPubKey().toHexKey())
|
||||
}
|
||||
|
||||
is NPub -> {
|
||||
account.cache.getOrCreateUser(parsed.hex)
|
||||
}
|
||||
|
||||
is NProfile -> {
|
||||
val user = account.cache.getOrCreateUser(parsed.hex)
|
||||
parsed.relay.forEach { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
}
|
||||
user
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
user
|
||||
}
|
||||
}.getOrNull()
|
||||
} else if (term.startsWithAny(userUriPrefixes)) {
|
||||
runCatching {
|
||||
Nip19Parser.uriToRoute(term)?.entity?.let { parsed ->
|
||||
when (parsed) {
|
||||
is NSec -> {
|
||||
account.cache.getOrCreateUser(parsed.toPubKey().toHexKey())
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
is NPub -> {
|
||||
account.cache.getOrCreateUser(parsed.hex)
|
||||
}
|
||||
term.length == 64 && Hex.isHex64(term) -> {
|
||||
account.cache.getOrCreateUser(term)
|
||||
}
|
||||
|
||||
is NProfile -> {
|
||||
val user = account.cache.getOrCreateUser(parsed.hex)
|
||||
parsed.relay.forEach { relay ->
|
||||
account.cache.relayHints.addKey(user.pubkey(), relay)
|
||||
}
|
||||
user
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}.getOrNull()
|
||||
} else if (term.length == 64 && Hex.isHex64(term)) {
|
||||
account.cache.getOrCreateUser(term)
|
||||
} else {
|
||||
null
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
|
||||
+28
-22
@@ -168,34 +168,40 @@ fun VideoFeedLoaded(
|
||||
key = { _, item -> item.idHex },
|
||||
contentType = { _, item -> item.event?.kind ?: -1 },
|
||||
) { _, item ->
|
||||
if (item.event is PictureEvent) {
|
||||
PictureCardCompose(
|
||||
baseNote = item,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
when {
|
||||
item.event is PictureEvent -> {
|
||||
PictureCardCompose(
|
||||
baseNote = item,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
)
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
} else if (item.event is VideoEvent) {
|
||||
VideoCardCompose(item, accountViewModel, nav)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
)
|
||||
item.event is VideoEvent -> {
|
||||
VideoCardCompose(item, accountViewModel, nav)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
} else if (item.event is FileHeaderEvent) {
|
||||
FileHeaderCardCompose(item, accountViewModel, nav)
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
)
|
||||
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
item.event is FileHeaderEvent -> {
|
||||
FileHeaderCardCompose(item, accountViewModel, nav)
|
||||
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user