-
@ f03df3d4:a4d4f676
2025-01-28 16:42:57
Block 881223
4 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ f03df3d4:a4d4f676
2025-01-28 16:40:31
Block 881223
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 6be5cc06:5259daf0
2025-01-28 16:36:02
Então vou te chamar de sionista
-
@ 6be5cc06:5259daf0
2025-01-28 16:35:41
Infelizmente não tem mais a mesma relevância que o Império Romano tinha.
-
@ 3f751c3e:381a49ca
2025-01-28 16:34:20
Your attendance doesn’t define your discipline. Your attention does.
-
@ 6be5cc06:5259daf0
2025-01-28 16:34:02
Um pinto de perfil
-
@ 36bedab2:524acad4
2025-01-28 16:33:44
https://ucatholic.com/blog/how-seismology-the-study-of-earthquakes-became-known-as-the-jesuit-science/?utm_source=chatgpt.com
-
@ e7bf8dad:839ef3db
2025-01-28 16:32:50
Block 881223
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ cbab7074:f9f0bd61
2025-01-28 16:32:50
Block 881223
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ c6f7077f:ad5d48fd
2025-01-28 16:30:49
it’s truly emotional to see peter thiel, bill gates, musk, & zuck coming together to join trump’s team in fighting deep state, globalism, censorship, & the establishment. https://image.nostr.build/8e44dc56b0c3657b4074e0efcbb7fec1d729f589183918202ca19ea33829bf30.jpg
-
@ 449fdcb9:4fc6bc85
2025-01-28 16:29:42
E lembre-se, se me contestar ou contrariar, vou te acusar de antissemita. hihihi - tchau
-
@ df173277:4ec96708
2025-01-28 16:28:27
🚀 Maple AI Launch! Our end-to-end encrypted AI chat app, Maple AI, is now available! Try it for free and experience private AI conversations that sync automatically in private across your devices. https://m.primal.net/OEdQ.jpg https://trymaple.ai
48% of organizations and businesses risk security and intellectual property by entering non-public info into AI apps. Maple AI changes the game with end-to-end encryption for secure chats. Work and personal use, it's all private between you and the LLM.
Our server code is open source, so you can verify the security and integrity of our platform. Confidential computing ensures your conversations remain private, with cryptographic proof based on reproducible builds. Don't trust us to be honest, verify.
We offer flexible pricing plans: Free (10 chats/week), Starter ($5.99/month), and Pro ($20/month). Pay with credit card or Bitcoin. Prices are discounted until Jan 31st!
We wrote up more explaining the launch on our blog: https://blog.opensecret.cloud/maple-private-ai-for-work-and-personal
Are you a developer? Maple AI is the first app built on our revolutionary new platform, which provides a suite of secure tools for developers. With OpenSecret, you can add secure auth, private key management, encrypted data sync, private AI, and more to your own app. https://blog.opensecret.cloud/introducing-opensecret/
Sign up for Maple AI now and experience the future of secure and private AI. Our open-source technology and end-to-end encryption keep your conversations safe and private, on all your devices. https://trymaple.ai
-
@ 449fdcb9:4fc6bc85
2025-01-28 16:24:44
Igreja católica é uma versão demoníaca do Império Romano.
-
@ f7380c11:d5c75df4
2025-01-28 16:22:38
Good Morning Love 💕
Hold fast to what’s true and ignore everything that tries to disrupt your peace. Chaos and confusion are the enemy’s tools of destruction.
Stay vigilant. Use discernment. Be joyful🌻
#plebchain #coffeechain #positivitystr
-
@ 449fdcb9:4fc6bc85
2025-01-28 16:22:01
Sou descendente de judeus que adora os deuses pagãos, egípcios, etc... Eu sou suspeita pra me posicionar sobre, mas pela minha ótica os cristãos são idiotas úteis que seguem um judeu dissidente.
-
@ 9e4a5274:af8c4bc4
2025-01-28 16:20:47
Bom dia a todes, todxs, todys, toddys, todis, todex, todix e todyx🫂❤️
-
@ 36141fcd:f9f219b0
2025-01-28 16:17:24
#Brasil proíbe pagamento por coleta de íris da startup do fundador da OpenAI
#Órgão do governo que fiscaliza proteção de dados pessoais vê risco à livre manifestação da vontade com incentivo financeiro em projeto do World ID; medida entrou em vigor no sábado (25).
https://image.nostr.build/58b7eb3c14496b4faec247fd00de5f1e9c19323c7b0cda0b244d9a02885537e2.jpg https://image.nostr.build/58b7eb3c14496b4faec247fd00de5f1e9c19323c7b0cda0b244d9a02885537e2.jpg
A Autoridade Nacional de Proteçãode Dados proibiu a oferta de moeda digital mediante mediante a coleta de dados biométricos em território nacional. Dessa maneira o Brasil junta-se a outros países como França, Alemanha, Argentina, Hong Kong entre outros.
-
@ e7bf8dad:839ef3db
2025-01-28 16:10:46
Block 881219
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ fbe5e80e:aaf46077
2025-01-28 16:09:55
https://video.nostr.build/d9d72d9fc51f54169ff94f9496c645d6f67458b983969368be4845bbf14b236a.mp4
-
@ 6be5cc06:5259daf0
2025-01-28 16:07:15
Falam como se a Igreja Católica negasse a bíblia kkkkk
-
@ 6be5cc06:5259daf0
2025-01-28 16:05:53
Depende do contexto.
No contexto de Êxodo 20:4 (na Septuaginta), εἴδωλον se refere a uma imagem ou representação material, geralmente de um deus falso, que é adorado como uma divindade. No Novo Testamento, é usado para denotar objetos de adoração pagã e é associado à idolatria, que é a adoração de coisas criadas em vez do Criador, ou seja, um ídolo.
Como no link que coloquei ali, do BibleHub.
-
@ 472f440f:5669301e
2025-01-28 16:01:34
This should end well.
https://m.primal.net/OEbh.png
-
@ f03df3d4:a4d4f676
2025-01-28 16:00:47
Block 881218
5 - high priority
4 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ f8e6c643:03328ca9
2025-01-28 15:58:12
https://m.primal.net/OEbY.jpg
-
@ 805e3c98:17d9e329
2025-01-28 15:55:34
Yeah David is right, somewhere between 1g-3g per day. Depends on how you feel. Its a pretty safe substance, but probably stay under 5g.
-
@ b7cf9f42:ecb93e78
2025-01-28 15:51:58
Ok 🥲
For me the only thing I do not have with coracle or Yana, which is cool on Amethyst is the reactions. Once can react with whatever symbol instead of only giving a like.
-
@ 3f770d65:7a745b24
2025-01-28 15:50:08
With nostr:nprofile1qqsyv47lazt9h6ycp2fsw270khje5egjgsrdkrupjg27u796g7f5k0spzemhxue69uhk2er9dchxummnw3ezumrpdejz7qgwwaehxw309ahx7uewd3hkctcprdmhxue69uhkummnw3ez6vfwde3x7tnpdenkzmnf9e3k75xqss4's help, I too can post sexy #footstr photos, nostr:nprofile1qqst7gmku9a6fmpxn5g0ejvk536xk3g322lfqv06frn5257au4fxhnspzdmhxue69uhhwmm59e6hg7r09ehkuef0eyu698. 🫂💜🤙🏻 https://nostr-relay.derekross.me/ba7c0c0e8709b3c6a4bbca0ec2638a4d534901458a3dcaeaaa8fdfbdfb25bc13.jpg
nostr:nevent1qqszys369fd3ag00ze2f6r3clhn5m8ghgl73zxdrurg4jzahrqnaq8spzdmhxue69uhhwmm59e6hg7r09ehkuef0qgst7gmku9a6fmpxn5g0ejvk536xk3g322lfqv06frn5257au4fxhnsrqsqqqqqpuzczm2
-
@ 456f5df2:0f90578f
2025-01-28 15:47:12
NOW - DHS Secretary Noem: "Here in NYC this morning. We are getting the dirtbags off these streets."
https://hell.twtr.plus/media/fc255d04d2b73dcba163c6099f7ab37aef01c4ac70e65ad8aebf4ab90351de68.file
-
@ 97c70a44:ad98e322
2025-01-28 15:42:07
I only use coracle, so for me nostr is centralized
-
@ 449fdcb9:4fc6bc85
2025-01-28 15:37:37
Pesquisei sobre:
E Eidólons são seres espirituais ou entidades que existem em várias mitologias e sistemas religiosos. Eles são geralmente considerados como representações ou manifestações de deuses, espíritos ou ancestrais.
Características dos Eidólons:
Invisíveis: Eles normalmente não são visíveis para humanos comuns, exceto em certas circunstâncias ou por meio de rituais específicos.
Manifestações: Eidólons podem se manifestar fisicamente em várias formas, como animais, pessoas ou objetos.
Poderes Sobrenaturais: Eles possuem poderes sobrenaturais, como manipular elementos, curar ou causar danos.
Proteção: Eles podem atuar como protetores ou guias espirituais para indivíduos ou grupos.
Comunicação: Eles podem se comunicar com humanos por meio de sonhos, visões ou outras formas sutis.
Em Diferentes Culturas:
O conceito de eidólons aparece em várias culturas ao redor do mundo:
Mitologia Grega: Os gregos antigos acreditavam em eidólons como imagens espirituais dos mortos.
Xamanismo: Em muitas culturas xamânicas, os eidólons são considerados espíritos animais ou ancestrais que fornecem orientação e proteção.
Teosofia: A teosofia considera os eidólons como manifestações da consciência coletiva ou das energias universais.
RPGs de Fantasia: Nos RPGs de fantasia, eidólons são frequentemente retratados como criaturas poderosas que podem ser invocadas ou controladas por personagens.
Eidólons são seres espirituais ou entidades que representam ou manifestam poderes divinos ou sobrenaturais. Eles aparecem em várias mitologias e sistemas religiosos e são frequentemente associados a proteção, orientação e comunicação com o mundo espiritual.
-
@ f03df3d4:a4d4f676
2025-01-28 15:35:46
Block 881217
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 6be5cc06:5259daf0
2025-01-28 15:32:07
Mesma coisa, não?
-
@ 6be5cc06:5259daf0
2025-01-28 15:31:46
Um bando de socialistas.
-
@ 6be5cc06:5259daf0
2025-01-28 15:30:34
Eu descobri esse termo na versão Septuaginta de Exodus 20:4
οὐ ποιήσεις σεαυτῷ εἴδωλον οὐδὲ παντὸς ὁμοίωμα ὅσα ἐν τῷ οὐρανῷ ἄνω καὶ ὅσα ἐν τῇ γῇ κάτω καὶ ὅσα ἐν τοῖς ὕδασιν ὑποκάτω τῆς γῆς
Do original hebraico:
לֹא תַעֲשֶׂה־לְךָ פֶסֶל וְכָל־תְּמוּנָה אֲשֶׁר בַּשָּׁמַיִם מִמַּעַל וַאֲשֶׁר בָּאָרֶץ מִתָּחַת וַאֲשֶׁר בַּמַּיִם מִתַּחַת לָאָרֶץ׃
Como tradução de פֶסֶל (Pesel), que refere-se a uma imagem ou escultura feita para ser venerada, ou seja, um ídolo.
εἴδωλον (Eidolon), também significa "ídolo" ou uma representação visual de algo para adoração.
Veja: https://biblehub.com/greek/1497.htm
E: https://biblehub.com/hebrew/6459.htm
Em português e demais línguas ocidentais, foi traduzido como:
“Não farás para ti imagem de escultura, nem alguma semelhança do que há em cima nos céus, nem embaixo na terra, nem nas águas debaixo da terra.”
Quando a tradução original mais precisa seria:
“Não farás para ti um ídolo, nem qualquer representação, tanto do que está no céu acima, como do que está na terra abaixo, nem do que está nas águas debaixo da terra.”
-
@ 456f5df2:0f90578f
2025-01-28 15:30:17
NOW - Doomsday Clock set to 89 seconds to midnight.
https://hell.twtr.plus/media/1a47f0487ecba3feefee41babc3d3550ca0a29fbcb2234b57faebe682545e1db.file
-
@ f03df3d4:a4d4f676
2025-01-28 14:50:46
Block 881214
5 - high priority
4 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ e7bf8dad:839ef3db
2025-01-28 14:40:46
Block 881214
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ e9691125:3320261f
2025-01-28 14:39:52
GOOD MORNING NOSTR 🫡
-
@ 2b33b2ac:da9f24c5
2025-01-28 14:39:04
gm #nostr https://i.nostr.build/1zLyv47zqk4Gx6GM.jpg
-
@ e7bf8dad:839ef3db
2025-01-28 14:30:47
Block 881213
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ f03df3d4:a4d4f676
2025-01-28 14:30:46
Block 881213
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 1be93261:3bf69614
2025-01-28 14:27:46
https://i.nostr.build/aLoFDtVxWjdFLMD3.jpg
-
@ bac24cb2:77c73766
2025-01-28 14:25:05
Today Signal (1/28/2025):
1 Failed download:
- LEN: No data found for this date range, symbol may be delisted
***
I am testing something new, but I need your collaboration. Reply this message with up to 3 Stock Market Symbols, I will try to comment on each stock if I am Long, Short or Neutral, and why.
Try me!!
-
@ 38dbb9b0:7048cd5c
2025-01-28 14:24:39
😂
-
@ b8851a06:9b120ba1
2025-01-28 14:20:42
Entrepreneurship isn't just about starting businesses - it's about discovering new ways to serve consumer needs through the competitive market process. Entrepreneurs drive economic progress by identifying profit opportunities that arise from changes in consumer preferences and market conditions.
#AustrianEconomics #nostr awareness of the day.
-
@ bac24cb2:77c73766
2025-01-28 14:19:05
Today Signal (1/28/2025):
🟢⬆️ Long *LEN*
▶️ Buy to Open (Stop Loss): `138.05`
└─📈 Sell to Close
├─🛑 Stop Loss: `132.36`
└─💰 Take Profit (Limit): `138.50`
🔴⬇️ Short *TT*
▶️ Sell to Open (Stop Loss): `363.84`
└─📈 Buy to Close
├─🛑 Stop Loss: `400.86`
└─💰 Take Profit (Limit): `360.94`
🔴⬇️ Short *ETN*
▶️ Sell to Open (Stop Loss): `304.89`
└─📈 Buy to Close
├─🛑 Stop Loss: `368.98`
└─💰 Take Profit (Limit): `299.87`
🔴⬇️ Short *PWR*
▶️ Sell to Open (Stop Loss): `290.59`
└─📈 Buy to Close
├─🛑 Stop Loss: `358.03`
└─💰 Take Profit (Limit): `285.30`
🔴⬇️ Short *CDNS*
▶️ Sell to Open (Stop Loss): `284.35`
└─📈 Buy to Close
├─🛑 Stop Loss: `320.48`
└─💰 Take Profit (Limit): `281.52`
🔴⬇️ Short *CEG*
▶️ Sell to Open (Stop Loss): `268.90`
└─📈 Buy to Close
├─🛑 Stop Loss: `347.44`
└─💰 Take Profit (Limit): `262.74`
🔴⬇️ Short *AVGO*
▶️ Sell to Open (Stop Loss): `195.26`
└─📈 Buy to Close
├─🛑 Stop Loss: `244.70`
└─💰 Take Profit (Limit): `191.38`
🔴⬇️ Short *DLR*
▶️ Sell to Open (Stop Loss): `155.84`
└─📈 Buy to Close
├─🛑 Stop Loss: `180.50`
└─💰 Take Profit (Limit): `153.90`
🔴⬇️ Short *ORCL*
▶️ Sell to Open (Stop Loss): `152.04`
└─📈 Buy to Close
├─🛑 Stop Loss: `183.60`
└─💰 Take Profit (Limit): `149.57`
🔴⬇️ Short *VST*
▶️ Sell to Open (Stop Loss): `131.42`
└─📈 Buy to Close
├─🛑 Stop Loss: `191.11`
└─💰 Take Profit (Limit): `126.74`
🔴⬇️ Short *NVDA*
▶️ Sell to Open (Stop Loss): `116.18`
└─📈 Buy to Close
├─🛑 Stop Loss: `142.62`
└─💰 Take Profit (Limit): `114.11`
🔴⬇️ Short *ANET*
▶️ Sell to Open (Stop Loss): `97.05`
└─📈 Buy to Close
├─🛑 Stop Loss: `129.17`
└─💰 Take Profit (Limit): `94.53`
🔴⬇️ Short *NRG*
▶️ Sell to Open (Stop Loss): `94.46`
└─📈 Buy to Close
├─🛑 Stop Loss: `112.37`
└─💰 Take Profit (Limit): `93.05`
🔴⬇️ Short *PEG*
▶️ Sell to Open (Stop Loss): `80.06`
└─📈 Buy to Close
├─🛑 Stop Loss: `88.05`
└─💰 Take Profit (Limit): `79.44`
🔴⬇️ Short *APH*
▶️ Sell to Open (Stop Loss): `65.44`
└─📈 Buy to Close
├─🛑 Stop Loss: `77.19`
└─💰 Take Profit (Limit): `64.52`
🔴⬇️ Short *WMB*
▶️ Sell to Open (Stop Loss): `53.23`
└─📈 Buy to Close
├─🛑 Stop Loss: `59.57`
└─💰 Take Profit (Limit): `52.73`
🔴⬇️ Short *NI*
▶️ Sell to Open (Stop Loss): `35.95`
└─📈 Buy to Close
├─🛑 Stop Loss: `38.00`
└─💰 Take Profit (Limit): `35.79`
🔴⬇️ Short *KMI*
▶️ Sell to Open (Stop Loss): `27.23`
└─📈 Buy to Close
├─🛑 Stop Loss: `30.27`
└─💰 Take Profit (Limit): `26.99`
🟢⬆️ Long *T*
▶️ Buy to Open (Stop Loss): `24.40`
└─📈 Sell to Close
├─🛑 Stop Loss: `22.72`
└─💰 Take Profit (Limit): `24.53`
***
I am testing something new, but I need your collaboration. Reply this message with up to 3 Stock Market Symbols, I will try to comment on each stock if I am Long, Short or Neutral, and why.
Try me!!
-
@ 04c915da:3dfbecc9
2025-01-28 14:17:50
https://cdn.satellite.earth/aa8b4de55ee2c169226f04c1e2c254246a2020f8204377a041275319f60a2fe2.jpg
https://cdn.satellite.earth/2d0a2a1ce0c5bbb2f55e0d07acd86737a144e09d7d76d9d6aefbc3a4e529ef3f.jpg
-
@ e7bf8dad:839ef3db
2025-01-28 14:10:46
Block 881206
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 460c25e6:ef85065c
2025-01-28 14:09:48
Let's go!
nostr:nevent1qqsv8fstf4z4c3xvu2m4al90qsxmn3efw7yzfcwc4425vhmffyckqaspz4mhxue69uhhyetvv9ujuerpd46hxtnfduhsygplwuxkt5a8vj5utj6s8tsj8e3wcavc45p4mqmw92qs7wrh5azmyspsgqqqqqqscw063u
-
@ 3f770d65:7a745b24
2025-01-28 14:07:16
#Olas is leveling up. https://nostr-relay.derekross.me/e73f967d39b7f68a4b0cd2ffcea30c6e7fa63b36923841a6e83b553cd366ef33.jpg
https://nostr-relay.derekross.me/db506bf8c65cf3a92710d7abd9b058d2af995b64a3c248a1e432f1d42c3e24d2.jpg
-
@ 04c915da:3dfbecc9
2025-01-28 14:05:28
GOOD MORNING NOSTR.
LIVE FREE. 🫡
https://cdn.satellite.earth/0e2cfd300ee7f5e5ca2eeff451b1508adfc3acaf9a0e60019c1c36431dcf8ba8.mp4
-
@ f03df3d4:a4d4f676
2025-01-28 14:00:47
Block 881205
4 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 82341f88:fbfbe6a2
2025-01-28 13:57:03
deepseek gives nostr clients a huge advantage…if they choose to use it
-
@ d043c3a7:ba24b89e
2025-01-28 13:50:16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>zchan</title>
<style>
body { font-family: Arial, sans-serif; background: #ffe }
h1 {
margin-top: 0;
}
#list, .replies {
list-style: none;
padding: 0;
}
.replies {
margin-top: -0.5em;
margin-bottom: 1em;
}
li, #download-link-container {
background: #F0E0D6;
padding: .3em;
}
#download-link-container {
font-weight: bold;
}
li {
position: relative;
}
textarea {
display: block;
width: calc(100% - 3em);
height: 7em;
}
#content {
position: relative;
}
.date {
color: #555;
color: #555;
position: absolute;
right: 0;
bottom: 0;
margin: .2em;
}
.date span {
margin-left: .6em;
}
img {
max-width: 100%;
max-height: 20vh;
}
.loading,
.loading * {
cursor: wait;
}
.container { max-width: 1000px; margin: auto; border: 2px solid #ddd; border-radius: 1em; padding: 1em; }
.category, .thread, .message { margin-bottom: 1em; word-wrap: break-word; }
.thread.reply, .more {
margin-left: 3em;
margin-bottom: 10px;
}
.thread {
padding-bottom: 1.5em;
}
.pagination { display: flex; justify-content: center; margin-top: 1em; }
.pagination a { margin: 0 5px; }
.new-thread-form, .new-message-form { display: none; margin-top: 1em; }
#download-link-container {
display: none;
}
.threadTitle {
display: block;
overflow: hidden;
text-overflow: ellipsis;
background: #EEAA88;
padding: .2em;
white-space: nowrap;
}
.imgLink {
display: inline-block;
margin-right: .2em;
}
h2 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#actions {
position: absolute;
right: 0;
}
</style>
</head>
<body>
<div class="container">
<h1>zchan</h1>
<div id="content">Loading...</div>
<p id="download-link-container">
Download <a id="download-link" download="chan.html" href="#">chan.html</a> to run this app locally
</p>
</div>
<script src="https://unpkg.com/nostr-tools@2.7.2/lib/nostr.bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/image-blob-reduce@4.1.0/dist/image-blob-reduce.min.js"></script>
<script>
window.nostr = window.NostrTools
const reduce = new ImageBlobReduce()
const max_file_size = 12e4
const local = !location.href.startsWith("http")
const fileServers = [
"https://blossom.primal.net",
"https://nostr.download",
"https://blossom.f7z.io"
]
const fileRelays = [
"wss://nostr.swiss-enigma.ch",
"wss://nostr.cercatrova.me"
]
if(!local){
document.getElementById("download-link").href = location.pathname
document.getElementById("download-link-container").style.display = "block"
}
const maxWait = 1000
const catFreshDays = 10
const clientTags = []
let relays = []
let sockets = []
let queryIdCounter = 0
let activeThread
function init(){
relays.push("wss://nostr.swiss-enigma.ch")
relays.push("wss://nostr.cercatrova.me")
relays.push("wss://soloco.nl")
relays.push("wss://nostr.oxtr.dev")
relays.push("wss://relay.piazza.today")
relays.push("wss://junxingwang.org")
relays.push("wss://n.ok0.org")
relays.push("wss://nostr.data.haus")
relays.push("wss://bostr.bitcointxoko.com")
relays.push("wss://relaypag.es")
relays.push("wss://relay.nostr.watch")
relays.push("wss://relay.damus.io")
const contentDiv = document.getElementById('content')
contentDiv.innerText = "Connecting"
return new Promise(resolve => {
let openSockets = []
let resolved = false
for(const relay of relays){
const sock = new WebSocket(relay)
sock.onopen = function(){
if(resolved){
sock.close()
return
}
openSockets.push(this)
contentDiv.innerText = "Connected to " + openSockets.length + " relays"
if(openSockets.length >= 5){
sockets = openSockets
resolved = true
resolve()
}
}
sockets.push(sock)
}
})
}
function send(messageObject, limit, queryId, timeout) {
if(!timeout){
timeout = 1000
}
const st = new Date().getTime()
const query = JSON.stringify(messageObject)
const events = []
const activeSockets = [...sockets]
return new Promise(resolve => {
let resolved = false
for(const socket of sockets){
const sst = new Date().getTime()
socket.addEventListener("message", function(e){
if(resolved){
return
}
const data = JSON.parse(e.data)
if(data[0] == "EOSE" || data[0] == "OK"){
const sockIndex = activeSockets.indexOf(this)
if(sockIndex != -1){
activeSockets.splice(sockIndex, 1)
}
console.log("query @ " + this.url + " took ", new Date().getTime() - sst, "ms")
}
else if(data[0] == "EVENT" && data[1] == queryId && data.length > 2){
if(events.find(e => e.id == data[2].id)){
return
}
events.push(data[2])
}
if(activeSockets.length == 0){
console.log("send ok (2), query took ", new Date().getTime() - st, "ms", Array.from(events))
resolved = true
resolve(events)
return
}
})
socket.addEventListener("close", function(){
console.log("closed", this.url)
})
socket.send(query)
}
setTimeout(() => {
if(resolved){
return
}
console.log("send ok (3), query took ", new Date().getTime() - st, "ms")
console.log("timeouted relays", activeSockets.map(s => s.url))
resolved = true
resolve(Array.from(events))
}, timeout)
})
}
function text(str, multiline){
if(!str){
return str
}
str = str.replaceAll("<", "<").replaceAll("'", "‘").replaceAll("\"", """)
if(multiline){
str = str.replaceAll("\n", "<br/>")
}
return str
}
function showNewCategoryForm() {
const form = document.getElementById('new-category-form');
form.style.display = 'block';
}
function goToHashtag(){
const hashTags = document.querySelector("#hashtaginput").value.split(" ")
if(document.querySelector("#hashtaginput").value.length == 0){
return
}
const uri = hashTags.map(value => value.match(/^(#|)(.*)$/)[2]).join(",")
location.hash = "#tag/" + uri
}
function renderCategories() {
const usedNames = []
const contentDiv = document.getElementById('content')
const feedLink = localStorage.getItem("feed") && '<a href="#feed">view feed</a>' || ''
contentDiv.innerHTML = `
<h2>Topics</h2>
<form action="#" onsubmit="goToHashtag(); return false" autocomplete="on">
<p><input id="hashtaginput" type="text" placeholder="zchan aiart loli" required/></p>
<p><button>View threads</button></p>
</form>
${feedLink}
`
}
function saveFeed(){
localStorage.setItem("feed", location.hash.match("#tag/(.*)")[1])
location.hash = "#feed"
}
function createMessageElement(message, threadMessages, reply, reactions){
let threadTitleSafe = text(message.content)
const threadElement = document.createElement('li');
const date = text(new Date(message.created_at * 1000).toLocaleString("en-uk"))
threadElement.className = 'thread'
let repliesHtml = ""
if(reply){
threadElement.classList.add("reply")
}else{
repliesHtml = `
${reactions.length > 0 && `<span style="font-weight: bold">${reactions.length}</span> reactions` || ''}
<span style="${threadMessages.length > 1 ? "font-weight: bold": ""}">${threadMessages.length-1}</span> replies
`
}
threadElement.innerHTML = `
<a class="threadTitle" href="#thread/${text(threadMessages[0].id)}">${threadTitleSafe}</a> <span class="date">${date}${repliesHtml}</span>
<div class="messageContent">${links(text(message.content, false))}</div>
`
return threadElement
}
function escapeHref(uri){
return uri.replaceAll("'", "")
}
function makeUntilHash(categories, until){
if(location.hash.startsWith("#feed")){
return `#feed//${until}`
}
const tagsUri = categories.map(c => c.match(/^[^, ]+$/i)[0]).join(",")
return `#tag/${escapeHref(tagsUri)}/${until}`
}
function renderThreads(categories, threads, threadsMessages, threadsReactions) {
const categoriesHtml = makeCategoriesHtml(categories)
const categoriesValue = categories.map(c => c.match(/^[^, ]+$/i)[0]).join(" ")
const contentDiv = document.getElementById('content')
const feed = location.hash.startsWith("#feed") && localStorage.getItem("feed")
let feedLink = !feed && '<a href="#feed">view feed</a> ' || `<a href='#tag/${escapeHref(feed)}'>feed link</a>`
const feedButtonHtml = ! location.hash.startsWith("#feed") && '<button onclick="saveFeed()">Save feed</button>' || ''
let firstPageLinkHtml = ""
if(getUntil()){
firstPageLinkHtml = `<a href="${makeUntilHash(categories, "")}">first page</a>`
}
const lastTimestamp = Math.min(threads[threads.length-1].created_at, threadsMessages.length > 0 && threadsMessages[threadsMessages.length-1].created_at || Infinity) - 1
contentDiv.innerHTML = `
<h2><a href="#">Home</a> > ${categoriesHtml}</h2>
<div id="actions">${feedLink}${feedButtonHtml}</div>
<button onclick="showNewThreadForm('${categories[0]}', '${categories[0]}')">New Thread</button>
<div id="new-thread-form" class="new-thread-form">
<p><input type="text" id="new-thread-categories" placeholder="categories" value="${categoriesValue}"/>
<p><textarea id="new-thread-message" placeholder="First Message"></textarea></p>
<p><input id="new-thread-file" type="file"/> ${makeFileServerSelectionHtml()}</p>
<p><button onclick='createNewThread()'>Create Thread</button></p>
</div>
<ul id="list"></ul>
${firstPageLinkHtml}
<a onclick="window.scrollTo(0,0)" href="${location.origin + location.pathname + makeUntilHash(categories, lastTimestamp)}">next page >></a>
`
const list = document.getElementById("list")
threads.forEach(thread => {
let messages = [thread, ...threadsMessages.filter(m => m.tags.find(t => t[0] == "e" && t[1] == thread.id))]
let reactions = threadsReactions.filter(m => m.tags.find(t => t[0] == "e" && t[1] == thread.id))
const messageElement = createMessageElement(thread, messages, false, reactions)
list.appendChild(messageElement)
if(messages.length > 1){
const replyList = document.createElement("ul")
replyList.classList.add("replies")
const countDiff = messages.length - 4
if(countDiff > 0){
const el = document.createElement("li")
const link = document.createElement("a")
el.classList.add("more")
link.href = `#thread/${thread.id}`
link.innerText = `${countDiff} more messages`
el.append(link)
replyList.appendChild(el)
}
for(let message of messages.slice(1).slice(-3)){
//let reactions = threadsReactions.filter(m => m.tags.find(t => t[0] == "e" && t[1] == message.id))
const messageElement = createMessageElement(message, messages, true)
replyList.appendChild(messageElement)
}
list.append(replyList)
}
})
}
function showNewThreadForm(categoryId, categoryTitle) {
const form = document.getElementById('new-thread-form');
form.style.display = 'block';
}
async function sha256(blob) {
const uint8Array = new Uint8Array(await blob.arrayBuffer())
const hashBuffer = await crypto.subtle.digest('SHA-256', uint8Array)
const hashArray = Array.from(new Uint8Array(hashBuffer))
return hashArray.map((h) => h.toString(16).padStart(2, '0')).join('')
}
async function load_image(image_blob){
return new Promise((resolve) => {
let reader = new FileReader()
let data_url = reader.readAsDataURL(image_blob)
reader.onload = function(e){
const img = new Image()
img.src = e.target.result
img.onload = async () => resolve(img)
}
})
}
async function resize(max_size, quality, blob){
let resized_img = await reduce.toBlob(blob, { max: max_size })
return await reduce.pica.toBlob(await reduce.toCanvas(resized_img), 'image/webp', quality)
}
async function resizeImg(blob){
let quality = 0.90
let max_size = 3000
let min_size = 900
let min_quality = 0.75
let resized_img = null
while(resized_img == null || resized_img.size > max_file_size){
resized_img = await resize(max_size, quality, blob)
if(quality > min_quality){
quality = Math.max(min_quality, quality - 0.5)
}
else if(max_size > min_size){
max_size = Math.max(min_size, max_size - 500)
}
else {
break
}
}
let img = null
if(resized_img.size <= max_file_size){
img = await load_image(resized_img)
console.log("resized image file size: " + resized_img.size + " bytes, w: " + img.width + ", h: " + img.height + ", quality: " + quality)
}else{
throw "error: could not resize image within size limitations"
return
}
return resized_img
}
function blobToDataURL(blob, callback) {
return new Promise(resolve => {
var a = new FileReader()
a.onload = function(e) {
resolve(e.target.result)
}
a.readAsDataURL(blob)
})
}
async function uploadFile(fileBlob, privateKey){
const imgBlob = await resizeImg(fileBlob)
const fileDataUri = await blobToDataURL(imgBlob)
let fileEvent = {
"kind": 1063,
"content": "image",
"created_at": Math.floor(Date.now() / 1000),
"tags": [
["url", fileDataUri],
["m", imgBlob.type],
]
}
fileEvent = window.nostr.finalizeEvent(fileEvent, privateKey)
const fileId = fileEvent.id
await send(["EVENT", fileEvent], 1, "sendq", 5000)
const nevent_obj = Object.create(fileEvent)
nevent_obj.relays = fileRelays
const neventStr = window.nostr.nip19.neventEncode(nevent_obj)
return neventStr
}
async function uploadBlob(fileBlob, fileHash, privateKey, serverUrl){
let auth = {
"kind": 24242,
"content": "",
"created_at": Math.floor(new Date().getTime() / 1000), //Math.floor(new Date().getTime() / 1000 - Math.random() * 84000),
"tags": [
["t", "upload"],
["x", fileHash],
["expiration", (Math.floor(new Date().getTime() / 1000) + 10000).toString()]
]
}
auth = window.nostr.finalizeEvent(auth, privateKey)
const formData = new FormData()
formData.append("files", fileBlob)
const body = new Blob([fileBlob], { type: 'application/octet-stream' })
return await (await fetch(`${serverUrl}/upload`, {
method: 'PUT',
headers: {
'Content-Type': 'application/octet-stream',
'Authorization': 'Nostr ' + btoa(JSON.stringify(auth)),
},
body
})).json()
}
async function uploadAnyFile(fileBlob, fileHash, fileServers, privateKey){
const fileServerType = document.getElementById('form-file-server').value
if(fileBlob && fileBlob.type.startsWith("image/") && fileServerType == "event"){
const neventStr = await uploadFile(fileBlob, privateKey)
return ["nostr:" + neventStr]
}
const fileUrls = []
for(let serverUrl of fileServers){
const match = fileBlob.name.match(/\.[a-z0-9]{1,10}/)
fileExtension = match && match[0] || ""
const uploadResult = await uploadBlob(fileBlob, fileHash, privateKey, serverUrl)
fileUrls.push(`${serverUrl}/${uploadResult.sha256}${fileExtension}`)
}
return fileUrls
}
async function createNewThread() {
const categories = document.querySelector("#new-thread-categories").value.split(" ")
const messageInput = document.getElementById('new-thread-message')
const fileBlob = document.getElementById('new-thread-file').files[0]
let firstMessage = messageInput.value
if (!firstMessage && !fileBlob){
alert("message or file required")
return
}
const privateKey = window.nostr.generateSecretKey()
const uploadResult = fileBlob && await handleFileUpload(fileBlob, privateKey)
if(uploadResult && uploadResult.urls && uploadResult.urls.length > 0){
firstMessage = uploadResult.urls[0] + "\n" + firstMessage
}
const fileTags = uploadResult && uploadResult.tags || []
let messageEvent = {
kind: 1,
tags: [
...categories.map(category => ["t", category]),
...fileTags
],
content: firstMessage,
created_at: Math.floor(Date.now() / 1000),
pubkey: window.nostr.getPublicKey(privateKey),
}
messageEvent.id = window.nostr.getEventHash(messageEvent)
messageEvent = window.nostr.finalizeEvent(messageEvent, privateKey)
await send(["EVENT", messageEvent])
loadThreads(categories)
}
function links(str){
const img = '<a class="imgLink" href="$1" target="_blank"><img src="$1"/></a>'
str = str.replace(/(http(s|):\/\/\S+\.(jpg|jpeg|webp|png|gif)|blob:http(s|):\/\/\S+)/gi, img)
const embedHtml = '<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/$1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>'
str = str.replace(/https:\/\/www.youtube.com\/watch\?v=([a-zA-Z0-9]{11})/g, embedHtml)
str = (" "+str).replace(/(<br\/>| )(http(s|):\/\/[a-z0-9-_.+?%/&]+)/gi, '$1<a href="$2">$2</a>')
str = str.substr(1)
str = str.replaceAll("\n", "<br/>")
return str
}
function makeCategoriesHtml(categories){
return categories.map(category => {
const prefix = "tag"
const match = category.match(/^(#|)(.*)$/)
return `<a href="#${prefix}/${text(match[2])}">${text(match[2])}</a>`
}).join(" / ")
}
function makeFileServerSelectionHtml(idServer, idCount){
return `preferred server
<select id="form-file-server">
<option value="-1">default</option>
<option value="blob">http server</option>
<option value="event">event (base64)</option>
</select>
no of servers
<select id="form-file-server-count">
<option value="2">2 (default)</option>
<option value="1">1</option>
</select>
`
}
function renderMessages(messages, categories) {
console.log("renderMessages")
const categoriesHtml = makeCategoriesHtml(categories)
const contentDiv = document.getElementById('content');
contentDiv.innerHTML = `
<h2><a href="#">Home</a> > ${categoriesHtml} > ${text(messages[0].content)}</h2>
<ul id="list"></ul>
`
const list = document.getElementById("list")
messages.forEach(message => {
console.log("message", message)
const messageElement = document.createElement('li');
messageElement.className = 'message'
messageElement.innerHTML = `<div>${links(text(message.content, false))}</div>`
list.appendChild(messageElement);
})
activeThread = messages[0]
contentDiv.innerHTML += `
<button onclick="showNewMessageForm()">New Message</button>
<div id="new-message-form" class="new-message-form">
<p><textarea id="new-message-content" placeholder="New Message"></textarea></p>
<p><input id="new-message-file" type="file"/> ${makeFileServerSelectionHtml()}</p>
<p><button onclick='createNewMessage()'>Post Message</button></p>
</div>
`
}
function displayHtmlError(htmlError){
const contentDiv = document.getElementById('content')
contentDiv.innerHTML = `
<h2><a href="#">Home</a> > error</h2>
<p>${htmlError}</p>
`
}
function showNewMessageForm() {
const form = document.getElementById('new-message-form');
form.style.display = 'block';
}
function pickFileServers(){
const fileServerCount = document.getElementById('form-file-server-count').value
const selectedFileServers = []
const myFileServers = [...fileServers]
for(let i = 0; i < fileServerCount; i++){
const fileServerIndex = Math.round(Math.random()*(myFileServers.length-1))
selectedFileServers.push(...myFileServers.splice(fileServerIndex, 1))
}
return selectedFileServers
}
async function handleFileUpload(fileBlob, privateKey){
const tags = []
let urls
if(fileBlob){
const fileHash = await sha256(fileBlob)
const fileServers = pickFileServers()
urls = await uploadAnyFile(fileBlob, fileHash, fileServers, privateKey)
tags.push(["x", fileHash])
for(let fileServer of fileServers){
tags.push(["server", fileServer])
}
}
return {
tags,
urls
}
}
async function createNewMessage() {
const thread = activeThread
const messageInput = document.getElementById('new-message-content');
let messageContent = messageInput.value;
const fileBlob = document.getElementById('new-message-file').files[0]
if (!messageContent && !fileBlob){
alert("message or file required")
return
}
const privateKey = window.nostr.generateSecretKey()
const uploadResult = await handleFileUpload(fileBlob, privateKey)
if(uploadResult && uploadResult.urls && uploadResult.urls.length > 0){
messageContent = uploadResult.urls[0] + "\n" + messageContent
}
if(fileBlob && uploadResult && (!uploadResult.urls || uploadResult.urls.length == 0)){
//console.log("error", uploadResult, fileBlob)
alert("error")
return
}
const fileTags = uploadResult && uploadResult.tags || []
let event = {
kind: 1,
tags: [
['p', thread.pubkey],
['e', thread.id, relays[0], "root"],
...fileTags,
...clientTags
],
content: messageContent,
created_at: Math.floor(Date.now() / 1000),
pubkey: window.nostr.getPublicKey(privateKey),
};
event.id = window.nostr.getEventHash(event);
event = window.nostr.finalizeEvent(event, privateKey);
await send(["EVENT", event])
loadMessages(thread)
}
function fetchLatestMessages(kinds, limit) {
const queryId = (++queryIdCounter).toString()
return send(["REQ", queryId, { kinds: kinds, limit: limit }], limit, queryId)
}
function threadIdsFromMessages(messages){
const idsUnique = new Set(messages.map(msg => {
const root = msg.tags.find((t, i) => {
return t[0] == "e"
})
return root && root[1]
}).filter(id => id !== undefined && id.match(/^[a-z0-9]{64}$/)))
return Array.from(idsUnique)
}
function findThreadUpdateTime(thread, threadsMessages){
let latestTimestamp = 0
for(let message of [thread, ...threadsMessages]){
if(message != thread && !message.tags.find(t => t[0] == "e" && t[1] == thread.id)){
continue
}
if(message.created_at >= latestTimestamp){
latestTimestamp = message.created_at
}
}
return latestTimestamp
}
async function loadThreads(categories, redirect, until) {
const filters = { limit: 50 }
filters.kinds = [1]
filters["#t"] = categories
if(until){
filters["until"] = until
}
const latestMessages = await fetchLatestMessages([1], 500);
let queryId = (++queryIdCounter).toString()
let threads = await send(["REQ", queryId, filters], filters.limit, queryId)
threads = threads.filter(t => !t.tags.find(tag => tag[0] == "e"))
filters["ids"] = threadIdsFromMessages(latestMessages)
queryId = (++queryIdCounter).toString()
threads.push(...await send(["REQ", queryId, filters], filters.limit, queryId))
const threadsIds = []
let threadsUnique = []
for(let thread of threads){
if(!threadsIds.includes(thread.id)){
threadsIds.push(thread.id)
threadsUnique.push(thread)
}
}
queryId = (++queryIdCounter).toString()
const threadsEvents = await send(["REQ", queryId, { kinds: [1, 7], '#e': threadsIds, limit: 1000 }], 1000, queryId)
const threadsMessages = threadsEvents.filter(e => e.kind == 1)
const threadsReactions = threadsEvents.filter(e => e.kind == 7)
threadsMessages.sort((a, b) => a.created_at - b.created_at)
threadsUnique.sort((a, b) => {
aTimestamp = findThreadUpdateTime(a, threadsMessages)
bTimestamp = findThreadUpdateTime(b, threadsMessages)
return bTimestamp - aTimestamp;
})
await resolveEmbeddedMedia([...threadsUnique, ...threadsMessages])
if(threadsUnique.length == 0){
const feedLinkHtml = localStorage.getItem("feed") && ' or <a href="#feed">feed</a>' || ''
displayHtmlError(`no threads. try <a href="#">going to home</a>${feedLinkHtml}`)
return
}
renderThreads(categories, threadsUnique, threadsMessages, threadsReactions)
if(redirect != false){
const prefix = "tag"
const untilStr = until && `/${until}` || ""
window.location.hash = `#${prefix}/${categories.join(",")}${untilStr}`
}
}
async function resolveEmbeddedMedia(messages){
const matches = messages.map(m => m.content).join().matchAll(/nostr:(nevent[a-z0-9]+)/g)
const embeddedEventIds = []
const eventMap = {}
for(let m of [...matches]){
const neventStr = m[1]
let id
try {
id = nostr.nip19.decode(neventStr).data.id
}catch(e){
console.log("invalid nevent", neventStr)
}
eventMap[id] = neventStr
embeddedEventIds.push(id)
}
let queryId = (++queryIdCounter).toString()
const embeddedEvents = await send(["REQ", queryId, { ids: embeddedEventIds }], 200, queryId, 3000)
for(let event of embeddedEvents){
const urlTag = event.tags.find(t => t[0] == "url")
if(!urlTag){
continue
}
const dataUri = urlTag[1]
const neventStr = eventMap[event.id]
for(let message of messages){
const blob = await (await fetch(dataUri)).blob()
let blobUrl = URL.createObjectURL(blob)
message.content = message.content.replaceAll("nostr:" + neventStr, blobUrl)
}
}
}
async function loadMessages(thread) {
let queryId = (++queryIdCounter).toString()
let messages = await send(["REQ", queryId, { kinds: [1], '#e': [thread.id], limit: 200 }], 200, queryId)
messages.sort((a, b) => a.created_at - b.created_at)
const categories = []
for(let tag of thread.tags){
if(tag[0] == "t"){
categories.push(tag[1])
}
}
messages = [thread, ...messages]
await resolveEmbeddedMedia(messages)
renderMessages(messages, categories);
window.location.hash = `#thread/${thread.id}`
}
async function getThread(threadId) {
let queryId = (++queryIdCounter).toString()
const threads = await send(["REQ", queryId, { ids: [threadId], limit: 1 }], 1, queryId)
return threads.length > 0 ? threads[0] : null;
}
function getUntil(){
const hash = decodeURIComponent(window.location.hash)
const hashParts = hash.split('/')
if(hashParts.length > 2 && hashParts[2].match(/^\d+$/)){
return parseInt(hashParts[2], 10)
}
}
async function initializeFromHash() {
const hash = decodeURIComponent(window.location.hash)
const hashParts = hash.split('/')
let until = getUntil()
if (hash.startsWith('#thread/')) {
const threadId = hashParts[1]
const thread = await getThread(threadId)
if(!thread){
displayHtmlError('could not load thread. try <a href="javascript:location.reload()">reloading</a>')
return
}
await loadMessages(thread)
}
else if (hash.startsWith('#tag/')) {
const hashTagsStr = hashParts[1]
const categories = hashTagsStr.split(",")
await loadThreads(categories, false, until)
}
else if (hash.startsWith('#feed')){
const hashTagsStr = localStorage.getItem("feed")
const categories = hashTagsStr.split(",")
await loadThreads(categories, false, until)
}
else {
await renderCategories();
}
}
onhashchange = async function(){
document.body.classList.add("loading")
await initializeFromHash()
document.body.classList.remove("loading")
}
document.addEventListener('DOMContentLoaded', async () => {
await init()
await initializeFromHash();
});
</script>
</body>
</html>
-
@ 460c25e6:ef85065c
2025-01-28 13:41:36
Desktops don't deal well with the concept of "back". Only the browser does this correctly. Every other app has just a main screen and dialogs. And you "close the dialog", you don't go "back"
-
@ 460c25e6:ef85065c
2025-01-28 13:40:11
On Android, back is everything. We literally think about it all the time. Once you get used to it, you expect to go back to the previous screen regardless of app or if it is a modal or a drawer. It's all back.
On iOS, the back design is per app and usually reserved only for inner screens. Besides the back arrow on some inner screens, Dialogs tend also have an X button on the top right (not left) of the dialog. And then the drawers have their own gestures to match the drawing direction. Most importantly, "back" is not a thing for Android users. They are almost always moving forward (closing a dialog understood as moving forward).
-
@ a9434ee1:d5c885be
2025-01-28 13:38:45
Yup, swipe left (i.e. Back) is the default indeed.
And it's why I don't display (and waste space on) drag handle in modals.
iOS however doesn't have swipe left + back buttons are annoying, take space and are ugly. So I'm building out this other solution that leverages swipe down (a pattern common for closing pictures etc...) and opens up new possibilities that differentiate it from just "Back"
Just started coding this out 👇
https://cdn.satellite.earth/573f16d339a58358c298f6fc9f56489e77b4b4217ea0b968a957bff25c0de57d.png
On desktop there will be a "Back", a "History" and a "Home" button in the top toolbar to match these features.
-
@ 91bea5cd:1df4451c
2025-01-28 13:37:09
Tem tutorial?
Como fez? Isso é de utilidade pública 😁
-
@ f03df3d4:a4d4f676
2025-01-28 13:35:46
Block 881204
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 46fcbe30:6bd8ce4d
2025-01-28 13:34:38
"back" is often a tricky concept but what's the difference between iPhone and Android users?
A restaurant I frequent has an online menu where you can open a modal with details on dishes but when you click "back", the whole menu and your shopping cart is gone. Is that how it's supposed to work on iPhone?
-
@ 460c25e6:ef85065c
2025-01-28 13:32:31
Yeah, cases interfering with gestures are the sole reason why Android still offers the 3 nav buttons option on settings and why that is still the default for Samsung phones.
-
@ 81ed8130:77f5df3a
2025-01-28 13:30:34
[Transmission Confirmation] spiral degree cyclone visual - Chronicles of Narnia - Ben Barnes - if it’s not okay it’s not the end (nostr:npub1en8uevpryvw6sdha87rxq5trl3qmu5vqplsnyjx6xyjmx4cc2gwqtxl7d8 artwork association) - best part - shine 💛
~ for giggles from the 🧚🧚♀️ ~ 🎶there’s no place like home 💃✨✨
-
@ b203872c:32ca0390
2025-01-28 13:30:21
Can't even swipe down properly 'cause the phone-case has a border preventing me.
I love swipe gestures, used them a lot on Nova launcher to open folders or start actions on icons.
But swipe down at the bottom of the screen?
-
@ 4fe4a528:3ff6bf06
2025-01-28 13:22:35
The 2 million dollar task force recommends to stop injecting Albertians with covid-19 injections. #abpol nostr:nprofile1qy2hwumn8ghj7un9d3shjtnddaehgu3wwp6kyqpqtutz8kdkyqqtrvx5nhfm8yp94m5ny7ztgn3q4dfd523g9xxwnpcst4xtas
https://archive.is/5ZA6X#selection-2547.347-2547.348
-
@ 460c25e6:ef85065c
2025-01-28 13:20:17
Swipe down on Android? Only on Bottom drawer things, but people still just swipe left instead of down. In fact, I have seen people saying that swiping down on those elements doesn't make any sense. 😅
-
@ e7bf8dad:839ef3db
2025-01-28 13:10:46
Block 881202
6 - high priority
4 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ a9434ee1:d5c885be
2025-01-28 13:09:32
Swipe down fixes this :90percent:
-
@ 81ed8130:77f5df3a
2025-01-28 13:02:36
{minority report source egg interlinkage} ~ journal image shared - Triangle - He - She - We - spin spin spin like a wash cycle wheel ~ entering the roulette funnel as weeeeeeeeeee speak ~ 🌀🪬 🌎🌍🌏🪬🌀
[General] ~ remain steady, focused, clear, mindful and when in doubt ask yourself ‘would I wish this to happen to me’ and if the answer is no choose an option that does feel okay should the tables turn and flip and you find yourself on the receiving end of whatever ill thoughts & actions that may have arisen from deep within ~ 🫧🐙🔱🫧
#lylychronicles #open-source #lifeisart #artist #alchemizingahuman #emotionalprocessing #mentalhealth #mindbodyinterlinked #evolution
🏝️🌲🏡🌿🍃🌱🍂💨🌬️🪩🎆🎇🐣🐥🦆🎶
-
@ 00c5889b:3ea163c2
2025-01-28 13:02:07
👏🏼👏🏼👏🏼 nostr:note1gac0tpq57jjvh3tua8yz7qp4pt5zhnh29anp6tm93ttc9hml0mysay2n6a
-
@ 460c25e6:ef85065c
2025-01-28 12:57:40
You are not a good multiplatform developer until you learn how to property code back buttons/gestures between your iOS and Android flavors. The way users think about "going back" is different. It goes way beyond just a UI element. It's almost a way of life.
-
@ 7c765d40:bd121d84
2025-01-28 12:46:12
Another question for ya
What's one tip you'd give to someone who's somewhat new to nostr?
What are your nostr secrets?
Good replies will get a zap and get a shoutout on the show this morning!
https://image.nostr.build/c9bc05e4b3266be9c58a137e46af0f197cf6540351f12c790a41c6f8f2c1e43a.jpg
#asknostr #gm #coffeechain
-
@ 81ed8130:77f5df3a
2025-01-28 12:40:30
Noted Sound:: ~ 🎶 don’t make me your enemy ~ 🎶
-
@ 460c25e6:ef85065c
2025-01-28 12:40:12
Try https://nosta.me/
-
@ 14e75074:0050f61d
2025-01-28 12:38:15
Hey #asknostr what's your favorite way to link your nostr profile on the web?
I can put my pubkey up, but I'd prefer a link to conver more people to the nostr. Specifically something like #amethyst rather than web clients.
Mehbe something like njump along with a get started site?
-
@ e9ab3129:9c2d0b6d
2025-01-28 12:20:00
Trump Says Microsoft In Talks To Purchase TikTok
Trump Says Microsoft In Talks To Purchase TikTok
Late Monday night aboard Air Force One, President Donald Trump told reporters that Microsoft is in discussions with the China-based tech giant ByteDance to acquire TikTok, according to a https://www.bloomberg.com/news/articles/2025-01-28/trump-says-microsoft-eyeing-tiktok-bid-with-app-s-future-unclear
from Bloomberg.
"I would say yes," Trump told reporters when asked if Microsoft would purchase the short video app used by more than 170 million Americans.
The president continued, "A lot of interest in TikTok. There's great interest in TikTok."
Such a deal with Chinese owner ByteDance would avert a ban in the US. On Trump's first day in office, he signed an executive order extending the https://www.zerohedge.com/technology/goes-dark-sorry-tiktok-isnt-available-right-now
. This extension gives ByteDance sufficient time to negotiate a deal with a US company.
Last week, Trump told reporters he was open to https://www.zerohedge.com/technology/tiktok-dismisses-bloombergs-report-potential-sale-musk-pure-fiction
or Oracle founder Larry Ellison purchasing TikTok.
In recent days, AI startup Perplexity proposed a merger plan with TikTok, with the US government receiving half of the new company, a source told https://www.reuters.com/technology/trump-says-microsoft-is-talks-acquire-tiktok-2025-01-28/
.
Earlier Monday, Trump told House Republican leaders at the Trump National Doral just outside Miami that he previously pushed for a ban of the video app under national security grounds; however, he changed his mind due to pro-Trump content creators that flourished on the platform.
"We'll see what happens. We're going to have a lot of people bidding on it, and if we can save all that voice and all the jobs, and China won't be involved, we don't want China involved, but we'll see what happens," he told lawmakers, referring to TikTok.
Donald Trump said Microsoft is in talks to acquire TikTok and that he’d like to see a bidding war for the app. The US president has delayed enforcement of a ban on the app in the US, giving himself 75 days to decide on a course of action https://t.co/MWq0dGCuXp
— Reuters (@Reuters) https://twitter.com/Reuters/status/1884188419762647134?ref_src=twsrc%5Etfw
Trump noted, "I like bidding wars because you make your best deal. So if there's a bidding war, that's a good thing."
And Microsoft confirmed.
*TRUMP CONFIRMS MICROSOFT IN DISCUSSIONS ON TIKTOK
— zerohedge (@zerohedge) https://twitter.com/zerohedge/status/1884057517107630103?ref_src=twsrc%5Etfw
Shares of MSFT are muted in premarket trading in New York following the overnight report, as the https://www.zerohedge.com/markets/deepseek-may-trigger-negative-capex-impact-peak-data-center-forecasts-face-revision
to overshadow the entire AI complex.
https://cms.zerohedge.com/users/tyler-durden
Tue, 01/28/2025 - 07:20
https://www.zerohedge.com/technology/trump-says-microsoft-talks-purchase-tiktok
-
@ b0f01954:ff52249c
2025-01-28 12:17:01
As idiotas não iriam cancelar.
Ele poderia ficar ali parado, cobrando taxa de deslocamento e espera até elas cancelarem ou o Uber cancelar por não ter aparecido ninguém no local.
-
@ 91bea5cd:1df4451c
2025-01-28 12:15:38
Quando é homem é pedofilo, mas quando é mulher não há nada de errado.
https://blossom.primal.net/bf1b7680ce0c4d552f887d71cf65a65f26b77cd96a44b9a875f138c9f1b7bec0.mp4 https://blossom.primal.net/ff82e34426558dfa39b11bf6c463f17501da49080af45a29b526e1ae0f22ee9e.mp4
#Feminismo #Brasil
-
@ f03df3d4:a4d4f676
2025-01-28 12:15:18
Block 881198
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ f03df3d4:a4d4f676
2025-01-28 12:05:18
Block 881197
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ f03df3d4:a4d4f676
2025-01-28 12:00:21
Block 881196
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ cbab7074:f9f0bd61
2025-01-28 12:00:21
Block 881196
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 1ae011cb:1257a556
2025-01-28 12:00:21
Block 881196
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ f03df3d4:a4d4f676
2025-01-28 11:40:19
Block 881193
4 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ e7bf8dad:839ef3db
2025-01-28 11:40:19
Block 881193
4 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ e7bf8dad:839ef3db
2025-01-28 11:30:21
Block 881193
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 97da02b6:0b7c8747
2025-01-28 11:28:50
Sim. A igreja Católica chama de protestantismo, mas os evangélicos chamam de reformismo.
-
@ f03df3d4:a4d4f676
2025-01-28 11:25:18
Block 881193
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ e7bf8dad:839ef3db
2025-01-28 11:20:19
Block 881192
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 59909ec2:82db30b7
2025-01-28 11:20:16
Eu, se fosse o pai, colocaria a família da mulher toda contra ela e a deixaria em isolamento parental e exporia sua ação nas redes para ela não ter paz. Eu provavelmente seria processado, mas valeria a pena.
-
@ 460c25e6:ef85065c
2025-01-28 11:12:48
I don't think so, but using iOS gives Apple all your data anyway.
-
@ f03df3d4:a4d4f676
2025-01-28 11:10:18
Block 881192
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ f03df3d4:a4d4f676
2025-01-28 11:05:18
Block 881191
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ eda6dfd3:fbbd10af
2025-01-28 11:03:52
Maybe the driver was also on E, supposed to give you a lot of confidence
-
@ f03df3d4:a4d4f676
2025-01-28 11:00:21
Block 881191
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 1ae011cb:1257a556
2025-01-28 11:00:21
Block 881191
2 - high priority
2 - medium priority
2 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ fac513a1:d4f407a5
2025-01-28 10:59:21
Bitcoin vs Central Banking – the most important battle in the world today. James Check and I explore how Bitcoin is disrupting traditional finance and central banking. We discuss the impact of global liquidity, the role of key economic assets like the US dollar and oil, and why Bitcoin and gold are seen as the ultimate "fire alarms" in an uncertain economy.
https://v.nostr.build/8cIoRErLMrK5cYYa.mp4
-
@ f728d9e6:c8a38106
2025-01-28 10:45:58
Lord give me the confidence of a taxi driver embarking upon an hour long ride with the gas gauge already on E.
-
@ f03df3d4:a4d4f676
2025-01-28 10:45:18
Block 881189
3 - high priority
3 - medium priority
3 - low priority
2 - no priority
1 - purging
#bitcoinfees #mempool
-
@ 006532cb:fd8f28b4
2025-01-28 10:36:33
Revolution OS 2001
https://en.wikipedia.org/wiki/Revolution_OS
#GNU, #Linux, #Open-Source,
https://v.nostr.build/xCqc1N7AXMxvPzmu.mp4
-
@ 48b4fe6a:cdbdf5f4
2025-01-28 10:27:51
Regarding Alby Go. Does the fact that it needs to be downloaded from the iOS App Store mean that any balances on this wallet are potentially linked to your Apple ID?