Compare commits

..

1 Commits

Author SHA1 Message Date
376ab1d2c1 Fix build warnings
Changelog-Fixed: Fix build warnings
2023-07-02 17:32:33 -04:00
763 changed files with 10493 additions and 92477 deletions

2
.envrc
View File

@@ -1,4 +1,4 @@
use nix
#use nix
export TODO_FILE=$PWD/TODO

View File

@@ -1,35 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: 'Bug: '
labels: bug, Needs recreation
assignees: ''
---
**What happens**
When I perform action ___, _____ happens.
**What I expect to happen**
I expect _______ to happen.
**Link to noteID, npub**
Provide link to relevant noteID, npub etc.
**Screenshots/video recording**
If applicable, add screenshots to help explain your problem.
** Versions **
Damus version: [e.g. 1.7.2 (1()]
Operating system version: [e.g. iOS 17.2.1]
Device: e.g. iPhone 13 Pro
**Steps To Reproduce**
Steps to reproduce the behavior:
1. Open Damus
2. Tap on ___
3. Action ____
**Additional context**
Add any other context about the problem here.

View File

@@ -1,27 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: 'Feature Request:'
labels: feature
assignees: ''
---
## Have a go at filling out the User Story template below
As a Damus user who is _____________, I would like to _________________, so that I achieve ___________.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
** When does this problem happen? **
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

31
.github/workflows/run-tests.yaml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Run Test Suite
run-name: Testing ${{ github.ref }} by @${{ github.actor }}
on:
push:
branches:
- "master"
pull_request:
branches:
- "*"
jobs:
run_tests:
runs-on: macos-12
strategy:
matrix:
include:
- xcode: "14.2"
ios: "16.2"
name: Test iOS (${{ matrix.ios }})
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.xcode }}
- name: Run Tests
run: xcodebuild test -scheme damus -project damus.xcodeproj -destination 'platform=iOS Simulator,name=iPhone 14,OS=${{ matrix.ios }}' | xcpretty && exit ${PIPESTATUS[0]}

3
.gitignore vendored
View File

@@ -1,8 +1,5 @@
xcuserdata
/.direnv
damus/TestingPrivate.swift
damus.xcodeproj/xcshareddata/xcbaselines
.DS_Store
TODO.bak
tags
build-git-hash.txt

View File

@@ -1,7 +0,0 @@
Terry Yiu <git@tyiu.xyz> <963907+tyiu@users.noreply.github.com>
Ben Weeks <ben.weeks@knowall.ai> <ben.weeks@outlook.com>
Suhail Saqan <suhail.saqan@gmail.com> <43693074+suhailsaqan@users.noreply.github.com>
cr0bar <cr0bar@cr0.bar> <cr0bar@users.noreply.github.com>
Swift <scoder1747@gmail.com> <120697811+scoder1747@users.noreply.github.com>
Daniel D'Aquino <daniel@daquino.me> <patches@damus.io>
Transifex <transifex@transifex.com> <43880903+transifex-integration[bot]@users.noreply.github.com>

View File

@@ -1,467 +1,3 @@
## [1.7-rc2] - 2024-02-28
### Added
- Add support for Apple In-App purchases (Daniel DAquino)
- Notification reminders for Damus Purple impending expiration (Daniel DAquino)
- Damus Purple membership! (William Casarin)
- Fixed minor spacing and padding issues in onboarding views (ericholguin)
### Changed
- Disable inline text suggestions on 17.0 as they interfere with mention generation (William Casarin)
- EULA is not shown by default (ericholguin)
### Fixed
- Fix welcome screen not showing if the user enters the app directly after a successful checkout without going through the link (Daniel DAquino)
- Fix profile not updating bug (William Casarin)
- Fix nostrscripts not loading (William Casarin)
- Fix crash when accessing cached purple accounts (William Casarin)
- Hide member signup date on reposts (kernelkind)
- Fixed previews not rendering (ericholguin)
- Fix load media formatting on small screens (kernelkind)
- Fix shared nevents that are too long (kernelkind)
- Fix many nostrdb transaction related crashes (William Casarin)
### Removed
- Removed copying public key action (ericholguin)
[1.7-rc2]: https://github.com/damus-io/damus/releases/tag/v1.7-rc2
## [1.7-2] - 2024-01-24
### Added
- New fulltext search engine (William Casarin)
- Add "Always show onboarding suggestions" developer setting (Daniel DAquino)
- Add NIP-42 relay auth support (Charlie Fish)
- Add ability to hide suggested hashtags (ericholguin)
- Add ability to mute hashtag from SearchView (Charlie Fish)
- Add ability to preview media taken with camera (Suhail Saqan)
- Add ability to search for naddr, nprofiles, nevents (kernelkind)
- Add experimental push notification support (Daniel DAquino)
- Add naddr link support (kernelkind)
- Add regional relay recommendations to Relay configuration view (currently for Japanese users only) (Daniel DAquino)
- Add regional relays for Germany (Daniel DAquino)
- Add regional relays for Thailand (Daniel DAquino)
- Added a custom camera view (Suhail Saqan)
- Always convert damus.io links to inline mentions (William Casarin)
- Unfurl profile name on remote push notifications (Daniel DAquino)
- Zap notification support for push notifications (Daniel DAquino)
### Changed
- Generate nprofile/nevent links in share menus (kernelkind)
- Improve push notification support to match local notification support (Daniel DAquino)
- Move mute thread in menu so it's not clicked by accident (alltheseas)
- Prioritize friends when autocompleting (Charlie Fish)
### Fixed
- Add workaround to fix note language recognition and reduce wasteful translation requests (Terry Yiu)
- Allow mentioning users with punctuation characters in their names (kernelkind)
- Fix broken mentions when there is text is directly after (kernelkind)
- Fix crash on very large notes (Daniel DAquino)
- Fix crash when logging out and switching accounts (William Casarin)
- Fix duplicate notes getting written to nostrdb (William Casarin)
- Fix issue where adding relays might not work on corrupted contact lists (Charlie Fish)
- Fix onboarding post view not being dismissed under certain conditions (Daniel DAquino)
- Fix performance issue with gifs (William Casarin)
- Fix persistent local notifications even after logout (William Casarin)
- Fixed bug where sometimes notes from other profiles appear on profile pages (Charlie Fish)
- Remove extra space at the end of DM messages (kernelkind)
- Save current viewed image index when switching to fullscreen (kernelkind)
### Removed
- Removed old nsec key warning, nsec automatically convert to npub when posting (kernelkind)
[1.7-2]: https://github.com/damus-io/damus/releases/tag/v1.7-2
## [1.6-25] - 2023-10-31
### Added
- Tap to dismiss keyboard on user status view (ericholguin)
- Add setting that allows users to optionally disable the new profile action sheet feature (Daniel DAquino)
- Add follow button to profile action sheet (Daniel DAquino)
- Added reaction counters to nostrdb (William Casarin)
- Record when profile is last fetched in nostrdb (William Casarin)
### Changed
- Automatically load extra regional Japanese relays during account creation if user's region is set to Japan. (Daniel DAquino)
- Updated customize zap view (ericholguin)
- Users are now notified when you quote repost them (William Casarin)
- Save bandwidth by only fetching new profiles after a certain amount of time (William Casarin)
- Zap button on profile action sheet now zaps with a single click, while a long press brings custom zap view (Daniel DAquino)
### Fixed
- Use white font color in qrcode view (ericholguin)
- Fixed an issue where zapping would silently fail on default settings if the user does not have a lightning wallet preinstalled on their device. (Daniel DAquino)
[1.6-25]: https://github.com/damus-io/damus/releases/tag/v1.6-25
## [1.6-24] - 2023-10-22 - AppStore Rejection Cope
### Added
- Improve discoverability of profile zaps with zappability badges and profile action sheets (Daniel DAquino)
- Add suggested hashtags to universe view (Daniel DAquino)
- Suggest first post during onboarding (Daniel DAquino)
- Add expiry date for images in cache to be auto-deleted after a preset time to save space on storage (Daniel DAquino)
- Add QR scan nsec logins. (Jericho Hasselbush)
### Changed
- Improved status view design (ericholguin)
- Improve clear cache functionality (Daniel DAquino)
### Fixed
- Reduce size of event menu hitbox (William Casarin)
- Do not show DMs from muted users (Daniel DAquino)
- Add more spacing between display name and username, and prefix username with `@` character (Daniel DAquino)
- Broadcast quoted notes when posting a note with quotes (Daniel DAquino)
[1.6-24]: https://github.com/damus-io/damus/releases/tag/v1.6-24
## [1.6-23] - 2023-10-06 - Appstore Release
### Added
- Added merch store button to sidebar menu (Daniel DAquino)
### Changed
- Damus icon now opens sidebar (Daniel DAquino)
### Fixed
- Stop tab buttons from causing the root view to scroll to the top unless user is coming from another tab or already at the root view (Daniel DAquino)
- Fix profiles not updating (William Casarin)
- Fix issue where relays with trailing slashes cannot be removed (#1531) (Daniel DAquino)
[1.6-23]: https://github.com/damus-io/damus/releases/tag/v1.6-23
## [1.6-20] - 2023-10-04
### Changed
- Improve UX around clearing cache (Daniel DAquino)
- Show muted thread replies at the bottom of the thread view (#1522) (Daniel DAquino)
### Fixed
- Fix situations where the note composer cursor gets stuck in one place after tagging a user (Daniel DAquino)
- Fix some note composer issues, such as when copying/pasting larger text, and make the post composer more robust. (Daniel DAquino)
- Apply filters to hashtag search timeline view (Daniel DAquino)
- Hide quoted or reposted notes from people whom the user has muted. (#1216) (Daniel DAquino)
- Fix profile not updating (William Casarin)
- Fix small graphical toolbar bug when scrolling profiles (Daniel DAquino)
- Fix localization issues and export strings for translation (Terry Yiu)
[1.6-20]: https://github.com/damus-io/damus/releases/tag/v1.6-20
## [1.6-18] - 2023-09-21
### Added
- Add followed hashtags to your following list (Daniel DAquino)
- Add "Do not show #nsfw tagged posts" setting (Daniel DAquino)
- Hold tap to preview status URL (Jericho Hasselbush)
- Finnish translations (etrikaj)
### Changed
- Switch to nostrdb for @'s and user search (William Casarin)
- Use nostrdb for profiles (William Casarin)
- Updated relay view (ericholguin)
- Increase size of the hitbox on note ellipsis button (Daniel DAquino)
- Make carousel tab dots tappable (Bryan Montz)
- Move the "Follow you" badge into the profile header (Grimless)
### Fixed
- Fix text composer wrapping issue when mentioning npub (Daniel DAquino)
- Make blurred videos viewable by allowing blur to disappear once tapped (Daniel DAquino)
- Fix parsing issue with NIP-47 compliant NWC urls without double-slashes (Daniel DAquino)
- Fix padding of username next to pfp on some views (William Casarin)
- Fixes issue where username with multiple emojis would place cursor in strange position. (Jericho Hasselbush)
- Fixed audio in video playing twice (Bryan Montz)
- Fix crash when long pressing custom reactions (William Casarin)
- Fix random crashom due to old profile database (William Casarin)
[1.6-18]: https://github.com/damus-io/damus/releases/tag/v1.6-18
## [1.6-17] - 2023-08-23
### Added
- Add support for status URLs (William Casarin)
- Click music statuses to display in spotify (William Casarin)
- Add settings for disabling user statuses (William Casarin)
### Changed
- clear statuses if they only contain whitespace (William Casarin)
### Fixed
- Fix long status lines (William Casarin)
- Fix status events not expiring locally (William Casarin)
[1.6-17]: https://github.com/damus-io/damus/releases/tag/v1.6-17
## [1.6-16] - 2023-08-23
### Added
- Added live music statuses (William Casarin)
- Added generic user statuses (William Casarin)
### Fixed
- Avoid notification for zaps from muted profiles (tappu75e@duck.com)
- Fix text editing issues on characters added right after mention link (Daniel DAquino)
- Mute hellthreads everywhere (William Casarin)
[1.6-16]: https://github.com/damus-io/damus/releases/tag/v1.6-16
## [1.6-13] - 2023-08-18
### Fixed
- Fix bug where it would sometimes show -1 in replies (tappu75e@duck.com)
- Fix images and links occasionally appearing with escaped slashes (Daniel DAquino)
- Fixed nostrscript not working on smaller phones (William Casarin)
- Fix zaps sometimes not appearing (William Casarin)
- Fixed issue where reposts would sometimes repost the wrong thing (William Casarin)
- Fixed issue where sometimes there would be empty entries on your profile (William Casarin)
[1.6-13]: https://github.com/damus-io/damus/releases/tag/v1.6-13
## [1.6-11]: "Bugfix Sunday" - 2023-08-07
### Added
- Add close button to custom reactions (Suhail Saqan)
- Add ability to change order of custom reactions (Suhail Saqan)
- Adjustable font size (William Casarin)
### Changed
- Show renotes in Notes timeline (William Casarin)
### Fixed
- Ensure the person you're replying to is the first entry in the reply description (William Casarin)
- Don't cutoff text in notifications (William Casarin)
- Fix wikipedia url detection with parenthesis (William Casarin)
- Fixed old notifications always appearing on first start (William Casarin)
- Fix issue with slashes on relay urls causing relay connection problems (William Casarin)
- Fix rare crash triggered by local notifications (William Casarin)
- Fix crash when long-pressing reactions (William Casarin)
- Fixed nostr reporting decoding (William Casarin)
- Dismiss qr screen on scan (Suhail Saqan)
- Show QRCameraView regardless of same user (Suhail Saqan)
- Fix wiggle when long press reactions (Suhail Saqan)
- Fix reaction button breaking scrolling (Suhail Saqan)
- Fix crash when muting threads (Bryan Montz)
[1.6-11]: https://github.com/damus-io/damus/releases/tag/v1.6-11
## [1.6-8]: "nostrdb prep" 2023-08-03
### Added
- Suggested Users to Follow (Joel Klabo)
- Add support for multiple reactions (Suhail Saqan)
### Changed
- Improved memory usage and performance when processing events (William Casarin)
### Fixed
- Fixed disappearing text on iOS17 (cr0bar)
- Fix UTF support for hashtags (Daniel DAquino)
- Fix compilation error on test target in UserSearchCacheTests (Daniel DAquino)
- Fix nav crashing and buggyness (William Casarin)
- Allow relay logs to be opened in dev mode even if relay (Daniel D'Aquino)
- endless connection attempt loop after user removes relay (Bryan Montz)
[1.6-8]: https://github.com/damus-io/damus/releases/tag/v1.6-8
## 1.6 (7): "Less bad" - 2023-07-16
### Added
- Show nostr address username and support abbreviated _ usernames (William Casarin)
- Re-add nip05 badges to profiles (William Casarin)
- Add space when tagging users in posts if needed (William Casarin)
- Added padding under word count on longform account (William Casarin)
### Fixed
- Don't spam lnurls when validating zaps (William Casarin)
- Eliminate nostr address validation bandwidth on startup (William Casarin)
- Allow user to login to deleted profile (William Casarin)
- Fix issue where typing cc@bob would produce brokenb ccnostr:bob mention (William Casarin)
[1.6-7]: https://github.com/damus-io/damus/releases/tag/v1.6-7
## [1.6-6] - 2023-07-16
### Added
- New markdown renderer (William Casarin)
- Added feedback when user adds a relay that is already on the list (Daniel D'Aquino)
### Changed
- Hide nsec when logging in (cr0bar)
- Remove nip05 on events (William Casarin)
- Rename NIP05 to "nostr address" (William Casarin)
### Fixed
- Fixed issue where hashtags were leaking in DMs (William Casarin)
- Fix issue with emojis next to hashtags and urls (William Casarin)
- relay detail view is not immediately available after adding new relay (Bryan Montz)
- Fix nostr:nostr:... bugs (William Casarin)
[1.6-6]: https://github.com/damus-io/damus/releases/tag/v1.6-6
## [1.6-4] - 2023-07-13
### Added
- Add the ability to follow hashtags (William Casarin)
### Changed
- Remove note size restriction for longform events (William Casarin)
### Fixed
- Hide users and hashtags from home timeline when you unfollow (William Casarin)
- Fixed a bug where following a user might not work due to poor connectivity (William Casarin)
- Icon color for developer mode setting is incorrect in low-light mode (Bryan Montz)
- Fixed nav bar color on login, eula, and account creation (ericholguin)
### Removed
- Remove following Damus Will by default (William Casarin)
[1.6-4]: https://github.com/damus-io/damus/releases/tag/v1.6-4
## [1.6-3] - 2023-07-11
### Changed
- Start at top when reading longform events (William Casarin)
- Allow reposting and quote reposting multiple times (William Casarin)
### Fixed
- Show longform previews in notifications instead of the entire post (William Casarin)
- Fix padding on longform events (William Casarin)
- Fix action bar appearing on quoted longform previews (William Casarin)
[1.6-3]: https://github.com/damus-io/damus/releases/tag/v1.6-3
## [1.6-2] - 2023-07-11
### Added
- Add support for multilingual hashtags (cr0bar)
- Add r tag when mentioning a url (William Casarin)
- Add initial longform note support (William Casarin)
- Enable banner image editing (Joel Klabo)
- Add relay log in developer mode (Bryan Montz)
### Fixed
- Fix lag when creating large posts (William Casarin)
- Fix npub mentions failing to parse in some cases (William Casarin)
- Fix PostView initial string to skip mentioning self when on own profile (Terry Yiu)
- Fix freezing bug when tapping Developer settings menu (Terry Yiu)
- Fix potential fake profile zap attacks (William Casarin)
- Fix issue where malicious zappers can send fake zaps to another user's posts (William Casarin)
- Fix profile post button mentions (cr0bar)
- Fix icons on settings view (cr0bar)
- Fix Invalid Zap bug in reposts (William Casarin)
### Removed
- Remove old @ and & hex key mentions (William Casarin)
[1.6-2]: https://github.com/damus-io/damus/releases/tag/v1.6-2
## [1.6] - 2023-07-04
### Added
- Speed up user search (Terry Yiu)
- Add post button to profile pages (William Casarin)
- Add post button when logged in with private key and on own profile view (Terry Yiu)
### Changed
- Drop iOS15 support (Scott Penrose)
### Fixed
- Load more content on profile view (William Casarin)
- Fix reports to conform to NIP-56 (Terry Yiu)
- Fix profile navigation bugs from muted users list and relay list views (Terry Yiu)
- Fix navigation to translation settings view (Terry Yiu)
- Fixed all navigation issues (Scott Penrose)
- Disable post button when media upload in progress (Terry Yiu)
- Fix taps on mentions in note drafts to not redirect to other Nostr clients (Terry Yiu)
- Fix missing profile zap notification text (Terry Yiu)
[1.6]: https://github.com/damus-io/damus/releases/tag/v1.6
## [1.5-5] - 2023-06-24
### Fixed
@@ -1743,3 +1279,4 @@
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.damus</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
</dict>
</plist>

View File

@@ -1,45 +0,0 @@
//
// NotificationExtensionState.swift
// DamusNotificationService
//
// Created by Daniel DAquino on 2023-11-27.
//
import Foundation
struct NotificationExtensionState: HeadlessDamusState {
let ndb: Ndb
let settings: UserSettingsStore
let contacts: Contacts
let mutelist_manager: MutelistManager
let keypair: Keypair
let profiles: Profiles
let zaps: Zaps
let lnurls: LNUrls
init?() {
guard let ndb = try? Ndb(owns_db_file: false) else { return nil }
self.ndb = ndb
guard let keypair = get_saved_keypair() else { return nil }
// dumb stuff needed for property wrappers
UserSettingsStore.pubkey = keypair.pubkey
self.settings = UserSettingsStore()
self.contacts = Contacts(our_pubkey: keypair.pubkey)
self.mutelist_manager = MutelistManager(user_keypair: keypair)
self.keypair = keypair
self.profiles = Profiles(ndb: ndb)
self.zaps = Zaps(our_pubkey: keypair.pubkey)
self.lnurls = LNUrls()
}
@discardableResult
func add_zap(zap: Zapping) -> Bool {
// store generic zap mapping
self.zaps.add_zap(zap: zap)
return true
}
}

View File

@@ -1,136 +0,0 @@
//
// NotificationFormatter.swift
// DamusNotificationService
//
// Created by Daniel DAquino on 2023-11-13.
//
import Foundation
import UserNotifications
struct NotificationFormatter {
static var shared = NotificationFormatter()
// MARK: - Formatting with NdbNote
func format_message(event: NdbNote) -> UNMutableNotificationContent? {
let content = UNMutableNotificationContent()
if let event_json_data = try? JSONEncoder().encode(event), // Must be encoded, as the notification completion handler requires this object to conform to `NSSecureCoding`
let event_json_string = String(data: event_json_data, encoding: .utf8) {
content.userInfo = [
NDB_NOTE_JSON_USER_INFO_KEY: event_json_string
]
}
switch event.known_kind {
case .text:
content.title = NSLocalizedString("Someone posted a note", comment: "Title label for push notification where someone posted a note")
content.body = event.content
break
case .dm:
content.title = NSLocalizedString("New message", comment: "Title label for push notifications where a direct message was sent to the user")
content.body = NSLocalizedString("(Contents are encrypted)", comment: "Label on push notification indicating that the contents of the message are encrypted")
break
case .like:
guard let reactionEmoji = to_reaction_emoji(ev: event) else {
content.title = NSLocalizedString("Someone reacted to your note", comment: "Generic title label for push notifications where someone reacted to the user's post")
break
}
content.title = NSLocalizedString("New note reaction", comment: "Title label for push notifications where someone reacted to the user's post with a specific emoji")
content.body = String(format: NSLocalizedString("Someone reacted to your note with %@", comment: "Body label for push notifications where someone reacted to the user's post with a specific emoji"), reactionEmoji)
break
case .zap:
content.title = NSLocalizedString("Someone zapped you ⚡️", comment: "Title label for a push notification where someone zapped the user")
break
default:
return nil
}
return content
}
// MARK: - Formatting with LocalNotification
func format_message(displayName: String, notify: LocalNotification) -> (content: UNMutableNotificationContent, identifier: String)? {
let content = UNMutableNotificationContent()
var title = ""
var identifier = ""
switch notify.type {
case .mention:
title = String(format: NSLocalizedString("Mentioned by %@", comment: "Mentioned by heading in local notification"), displayName)
identifier = "myMentionNotification"
case .repost:
title = String(format: NSLocalizedString("Reposted by %@", comment: "Reposted by heading in local notification"), displayName)
identifier = "myBoostNotification"
case .like:
title = String(format: NSLocalizedString("%@ reacted with %@", comment: "Reacted by heading in local notification"), displayName, to_reaction_emoji(ev: notify.event) ?? "")
identifier = "myLikeNotification"
case .dm:
title = displayName
identifier = "myDMNotification"
case .zap, .profile_zap:
// not handled here. Try `format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)?`
return nil
}
content.title = title
content.body = notify.content
content.sound = UNNotificationSound.default
content.userInfo = notify.to_lossy().to_user_info()
return (content, identifier)
}
func format_message(displayName: String, notify: LocalNotification, state: HeadlessDamusState) async -> (content: UNMutableNotificationContent, identifier: String)? {
// Try sync method first and return if it works
if let sync_formatted_message = self.format_message(displayName: displayName, notify: notify) {
return sync_formatted_message
}
// If it does not work, try async formatting methods
let content = UNMutableNotificationContent()
switch notify.type {
case .zap, .profile_zap:
guard let zap = await get_zap(from: notify.event, state: state) else {
return nil
}
content.title = Self.zap_notification_title(zap)
content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
content.sound = UNNotificationSound.default
content.userInfo = LossyLocalNotification(type: .zap, mention: .note(notify.event.id)).to_user_info()
return (content, "myZapNotification")
default:
// The sync method should have taken care of this.
return nil
}
}
// MARK: - Formatting zap utility notifications
static func zap_notification_title(_ zap: Zap) -> String {
if zap.private_request != nil {
return NSLocalizedString("Private Zap", comment: "Title of notification when a private zap is received.")
} else {
return NSLocalizedString("Zap", comment: "Title of notification when a non-private zap is received.")
}
}
static func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
let src = zap.request.ev
let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey
let profile_txn = profiles.lookup(id: pk)
let profile = profile_txn?.unsafeUnownedValue
let name = Profile.displayName(profile: profile, pubkey: pk).displayName.truncate(maxLength: 50)
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
let formattedSats = format_msats_abbrev(zap.invoice.amount)
if src.content.isEmpty {
let format = localizedStringFormat(key: "zap_notification_no_message", locale: locale)
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name)
} else {
let format = localizedStringFormat(key: "zap_notification_with_message", locale: locale)
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats, name, src.content)
}
}
}

View File

@@ -1,87 +0,0 @@
//
// NotificationService.swift
// DamusNotificationService
//
// Created by Daniel DAquino on 2023-11-10.
//
import UserNotifications
import Foundation
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
guard let nostr_event_json = request.content.userInfo["nostr_event"] as? String,
let nostr_event = NdbNote.owned_from_json(json: nostr_event_json)
else {
// No nostr event detected. Just display the original notification
contentHandler(request.content)
return;
}
// Log that we got a push notification
Log.debug("Got nostr event push notification from pubkey %s", for: .push_notifications, nostr_event.pubkey.hex())
guard let state = NotificationExtensionState(),
let display_name = state.ndb.lookup_profile(nostr_event.pubkey)?.unsafeUnownedValue?.profile?.display_name // We are not holding the txn here.
else {
// Something failed to initialize so let's go for the next best thing
guard let improved_content = NotificationFormatter.shared.format_message(event: nostr_event) else {
// We cannot format this nostr event. Suppress notification.
contentHandler(UNNotificationContent())
return
}
contentHandler(improved_content)
return
}
// Don't show notification details that match mute list.
// TODO: Remove this code block once we get notification suppression entitlement from Apple. It will be covered by the `guard should_display_notification` block
if state.mutelist_manager.is_event_muted(nostr_event) {
// We cannot really suppress muted notifications until we have the notification supression entitlement.
// The best we can do if we ever get those muted notifications (which we generally won't due to server-side processing) is to obscure the details
let content = UNMutableNotificationContent()
content.title = NSLocalizedString("Muted event", comment: "Title for a push notification which has been muted")
content.body = NSLocalizedString("This is an event that has been muted according to your mute list rules. We cannot suppress this notification, but we obscured the details to respect your preferences", comment: "Description for a push notification which has been muted, and explanation that we cannot suppress it")
content.sound = UNNotificationSound.default
contentHandler(content)
return
}
guard should_display_notification(state: state, event: nostr_event, mode: .push) else {
// We should not display notification for this event. Suppress notification.
// contentHandler(UNNotificationContent())
// TODO: We cannot really suppress until we have the notification supression entitlement. Show the raw notification
contentHandler(request.content)
return
}
guard let notification_object = generate_local_notification_object(from: nostr_event, state: state) else {
// We could not process this notification. Probably an unsupported nostr event kind. Suppress.
// contentHandler(UNNotificationContent())
// TODO: We cannot really suppress until we have the notification supression entitlement. Show the raw notification
contentHandler(request.content)
return
}
Task {
if let (improvedContent, _) = await NotificationFormatter.shared.format_message(displayName: display_name, notify: notification_object, state: state) {
contentHandler(improvedContent)
}
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>1C8F.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -1,13 +0,0 @@
all: nostrscript/primal.wasm
nostrscript/%.wasm: nostrscript/%.ts nostrscript/nostr.ts Makefile
asc $< --runtime stub --outFile $@ --optimize
tags:
find damus-c -name '*.c' -or -name '*.h' | xargs ctags
clean:
rm nostrscript/*.wasm
.PHONY: tags

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>1C8F.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -1,125 +0,0 @@
{
"identifier" : "64C21A2D",
"nonRenewingSubscriptions" : [
],
"products" : [
],
"settings" : {
"_applicationInternalID" : "1628663131",
"_developerTeamID" : "XK7H4JAB3D",
"_failTransactionsEnabled" : false,
"_lastSynchronizedDate" : 704848066.26849198,
"_locale" : "en_US",
"_storefront" : "USA",
"_storeKitErrors" : [
{
"current" : null,
"enabled" : false,
"name" : "Load Products"
},
{
"current" : null,
"enabled" : false,
"name" : "Purchase"
},
{
"current" : null,
"enabled" : false,
"name" : "Verification"
},
{
"current" : null,
"enabled" : false,
"name" : "App Store Sync"
},
{
"current" : null,
"enabled" : false,
"name" : "Subscription Status"
},
{
"current" : null,
"enabled" : false,
"name" : "App Transaction"
},
{
"current" : null,
"enabled" : false,
"name" : "Manage Subscriptions Sheet"
},
{
"current" : null,
"enabled" : false,
"name" : "Refund Request Sheet"
},
{
"current" : null,
"enabled" : false,
"name" : "Offer Code Redeem Sheet"
}
]
},
"subscriptionGroups" : [
{
"id" : "21283177",
"localizations" : [
],
"name" : "Purple",
"subscriptions" : [
{
"adHocOffers" : [
],
"codeOffers" : [
],
"displayPrice" : "6.99",
"familyShareable" : false,
"groupNumber" : 1,
"internalID" : "6446591615",
"introductoryOffer" : null,
"localizations" : [
{
"description" : "Support damus development with Damus Purple!",
"displayName" : "Damus Purple",
"locale" : "en_CA"
}
],
"productID" : "purple",
"recurringSubscriptionPeriod" : "P1M",
"referenceName" : "Purple",
"subscriptionGroupID" : "21283177",
"type" : "RecurringSubscription"
},
{
"adHocOffers" : [
],
"codeOffers" : [
],
"displayPrice" : "69.99",
"familyShareable" : false,
"groupNumber" : 2,
"internalID" : "6448764101",
"introductoryOffer" : null,
"localizations" : [
],
"productID" : "purpleyearly",
"recurringSubscriptionPeriod" : "P1Y",
"referenceName" : "Purple Yearly",
"subscriptionGroupID" : "21283177",
"type" : "RecurringSubscription"
}
]
}
],
"version" : {
"major" : 3,
"minor" : 0
}
}

View File

@@ -2,56 +2,26 @@
# damus
A twitter-like [nostr][nostr] client for iPhone, iPad and MacOS.
A twitter-like [nostr][nostr] client for iPhone, iPad and MacOS.
<img src="./ss.png" width="50%" height="50%" />
[nostr]: https://github.com/fiatjaf/nostr
## How is Damus better than twitter?
There are no toxic algorithms.\
You can send or receive zaps (satoshis) without asking for permission.\
[There is no central database](https://fiatjaf.com/nostr.html). Therefore, Damus is censorship resistant.\
There are no ads.\
You don't have to reveal sensitive personal information to sign up.\
No email is required. \
No phone number is required. \
Damus is free and open source software. \
There is no Big Tech moat. Therefore, seamless interoperability with thousands or millions of other nostr apps is possible, and is how [Damus and nostr win](https://www.youtube.com/watch?v=qTixqS-W1yo).
## If there are no ads, how is Damus funded?
Damus offers a paid subscription 🟣 purple 🟣 https://damus.io/purple/. \
Initial benefits include a unique subscriber number, subscriber badge, and auto-translate powered by DeepL.
Damus has also graciously received donations or grants from hundreds of Damus users, [Opensats](https://opensats.org/), and the [Human Rights Foundation](https://hrf.org/).
## Spec Compliance
damus implements the following [Nostr Implementation Possibilities][nips]
- [NIP-01: Basic protocol flow][nip01]
- [NIP-04: Encrypted direct message][nip04]
- [NIP-08: Mentions][nip08]
- [NIP-10: Reply conventions][nip10]
- [NIP-12: Generic tag queries (hashtags)][nip12]
- [NIP-19: bech32-encoded entities][NIP19]
- [NIP-21: nostr: URI scheme][NIP21]
- [NIP-25: Reactions][NIP25]
- [NIP-42: Authentication of clients to relays][nip42]
- [NIP-56: Reporting][nip56]
[nips]: https://github.com/nostr-protocol/nips
[nip01]: https://github.com/nostr-protocol/nips/blob/master/01.md
[nip04]: https://github.com/nostr-protocol/nips/blob/master/04.md
[nip08]: https://github.com/nostr-protocol/nips/blob/master/08.md
[nip10]: https://github.com/nostr-protocol/nips/blob/master/10.md
[nip12]: https://github.com/nostr-protocol/nips/blob/master/12.md
[nip19]: https://github.com/nostr-protocol/nips/blob/master/19.md
[nip21]: https://github.com/nostr-protocol/nips/blob/master/21.md
[nip25]: https://github.com/nostr-protocol/nips/blob/master/25.md
[nip42]: https://github.com/nostr-protocol/nips/blob/master/42.md
[nip56]: https://github.com/nostr-protocol/nips/blob/master/56.md
## Getting Started on Damus
@@ -62,7 +32,7 @@ damus implements the following [Nostr Implementation Possibilities][nips]
- Relays: You can add more relays to send your notes to by tapping the "+".
- Find more relays to add: https://nostr.info/relays/
- Public Key (pubkey): Your public, personal address and how people can find and tag you
- Secret Key: Your *private* key unique to you. Never share your private key publicly and share with other clients at your own risk!
- Secret Key: Your *private* key unique to you. Never share your private key publically and share with other clients at your own risk!
- Save your keys somewhere safe
- Log out
@@ -76,15 +46,19 @@ damus implements the following [Nostr Implementation Possibilities][nips]
1. Search their username in the search bar at the top of the 🔍 Global Feed and click their profile
2. Tap the 🔑 icon which will copy their pubkey to your clipboard
3. Go back to your 🏠 Personal Feed and tap the blue + button to compose your Note
4. Add @ directly followed by the pubkey (e.g., `@npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s`)
- You can also tap the ellipsis menu of a Note (three dots in top right of note) to grab their User ID aka pubkey or Note ID to link directly to a Note.
4. Add @ direcly followed by the pubkey (e.g., `@npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s`)
- You can also long-press a Note to grab their User ID aka pubkey or Note ID to link directly to a Note.
- Currently you can't delete your Notes in the iOS app
- Share images by pasting the image url which you can grab from nostr.build, imgbb, imgur, etc. (i.e., `https://i.ibb.co/2SHZbwm/alpha60.jpg`). Currently images only load for people you follow in the 🏠 Personal Feed. Images are not automatically loaded in 🔍 Global Feed
- Share images by pasting the image url which you can grab from imgbb, imgur, etc. (i.e., `https://i.ibb.co/2SHZbwm/alpha60.jpg`). Currently images only load for people you follow in the 🏠 Personal Feed. Images are not automatically loaded in 🔍 Global Feed
- Engaging with Notes
- 💬 Replying to a Note: Tap the chat icon underneath the note. This will show up in the users notifications and in your 🏠 Personal and 🔍 Global Feeds
- ♺ Reposts: Tap the repost icon which will show up in your 🏠 Personal and 🔍 Global Feeds
- ♡ Likes: Tap the heart icon. Users will not get a notification, and cannot see who liked their note (currently, web clients can see your pfp only)
- Formatting Notes (may not format as intended in other web clients)
- Italics: 1 asterisk `*italic*`
- Bold: 2 asterisk `**bold**`
- Strikethrough: 1 tildes `~strikethrough~`
- Code: 1 back-tick `` `code` ``
#### 💬 Encrypted DMs (chat app, bottom navigation)
- Tap the chat icon and you'll notice there's nothing to see at first. Go to a user profile and tap the 💬 chat icon next to the follow button to begin a DM
@@ -102,9 +76,7 @@ damus implements the following [Nostr Implementation Possibilities][nips]
4. For PFP, insert a URL containing your image (support video: https://cdn.jb55.com/vid/pfp-editor.mp4)
5. Save
#### ⚡️ Request Sats
Paste an invoice from your favorite LN wallet.
(Sats or Satoshis are the smallest denomination of bitcoin)
**Alby (browser extension)**
@@ -136,17 +108,26 @@ We have a few mailing lists that anyone can join to get involved in damus develo
[product-list]: https://damus.io/list/product
[design-list]: https://damus.io/list/design
### Contributing
### Code
See [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md)
[Email patches][git-send-email] to patches@damus.io are preferred, but I accept PRs on GitHub as well. Patches sent via email may include a bolt11 lightning invoice, choosing the price you think the patch is worth, and I will pay it once the patch is accepted and if I think the price isn't unreasonable. You can also send an any-amount invoice and I will pay what I think it's worth if you prefer not to choose. You can include the bolt11 in the commit body or email so that it can be paid once it is applied.
Recommended settings when submitting code via email:
```
$ git config sendemail.to "patches@damus.io"
$ git config format.subjectPrefix "PATCH damus"
$ git config --global sendemail.annotate yes
$ git config format.signOff yes
```
[git-send-email]: http://git-send-email.io
### Privacy
Your internet protocol (IP) address is exposed to the relays you connect to, and third party media hosters (e.g. nostr.build, imgur.com, giphy.com, youtube.com etc.) that render on Damus. If you want to improve your privacy, consider utilizing a service that masks your IP address (e.g. a VPN) from trackers online.
The relay also learns which public keys you are requesting, meaning your public key will be tied to your IP address.
It is public information which other profiles (npubs) you are exchanging DMs with. The content of the DMs is encrypted.
### Translations
Translators welcome! Join the [Transifex][transifex] project.
@@ -157,10 +138,8 @@ All user-facing strings must have a comment in order to provide context to trans
### Awards
Damus lead dev and founder Will awards developers with satoshis!
There may be nostr badges awarded for contributors in the future... :)
First contributors:
1. @randymcmillan

0
TODO
View File

View File

@@ -35,7 +35,7 @@ typedef struct mention_bech32_block {
struct nostr_bech32 bech32;
} mention_bech32_block_t;
typedef struct note_block {
typedef struct block {
enum block_type type;
union {
struct str_block str;
@@ -45,13 +45,12 @@ typedef struct note_block {
} block;
} block_t;
typedef struct note_blocks {
int words;
typedef struct blocks {
int num_blocks;
struct note_block *blocks;
struct block *blocks;
} blocks_t;
void blocks_init(struct note_blocks *blocks);
void blocks_free(struct note_blocks *blocks);
void blocks_init(struct blocks *blocks);
void blocks_free(struct blocks *blocks);
#endif /* block_h */

View File

@@ -1,131 +1,94 @@
//
// cursor.h
// damus
//
// Created by William Casarin on 2023-04-09.
//
#ifndef JB55_CURSOR_H
#define JB55_CURSOR_H
#ifndef cursor_h
#define cursor_h
#include "typedefs.h"
#include "varint.h"
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#define unlikely(x) __builtin_expect((x),0)
#define likely(x) __builtin_expect((x),1)
typedef unsigned char u8;
struct cursor {
unsigned char *start;
unsigned char *p;
unsigned char *end;
const u8 *p;
const u8 *start;
const u8 *end;
};
struct array {
struct cursor cur;
unsigned int elem_size;
};
static inline void reset_cursor(struct cursor *cursor)
{
cursor->p = cursor->start;
static inline int is_whitespace(char c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}
static inline void wipe_cursor(struct cursor *cursor)
{
reset_cursor(cursor);
memset(cursor->start, 0, cursor->end - cursor->start);
static inline int is_boundary(char c) {
return !isalnum(c);
}
static inline void make_cursor(u8 *start, u8 *end, struct cursor *cursor)
{
cursor->start = start;
cursor->p = start;
cursor->end = end;
static inline int is_invalid_url_ending(char c) {
return c == '!' || c == '?' || c == ')' || c == '.' || c == ',' || c == ';';
}
static inline void make_array(struct array *a, u8* start, u8 *end, unsigned int elem_size)
{
make_cursor(start, end, &a->cur);
a->elem_size = elem_size;
static inline int is_alphanumeric(char c) {
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
}
static inline int cursor_eof(struct cursor *c)
static inline void make_cursor(struct cursor *c, const u8 *content, size_t len)
{
return c->p == c->end;
c->start = content;
c->end = content + len;
c->p = content;
}
static inline void *cursor_malloc(struct cursor *mem, unsigned long size)
{
void *ret;
if (mem->p + size > mem->end) {
return NULL;
}
ret = mem->p;
mem->p += size;
return ret;
}
static inline void *cursor_alloc(struct cursor *mem, unsigned long size)
{
void *ret;
if (!(ret = cursor_malloc(mem, size))) {
return 0;
}
memset(ret, 0, size);
return ret;
}
static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size)
{
u8 *p;
if (!(p = cursor_alloc(mem, size))) {
return 0;
}
make_cursor(p, mem->p, slice);
return 1;
}
static inline void copy_cursor(struct cursor *src, struct cursor *dest)
{
dest->start = src->start;
dest->p = src->p;
dest->end = src->end;
}
static inline int cursor_skip(struct cursor *cursor, int n)
{
if (cursor->p + n >= cursor->end)
return 0;
cursor->p += n;
static inline int consume_until_boundary(struct cursor *cur) {
char c;
while (cur->p < cur->end) {
c = *cur->p;
if (is_boundary(c))
return 1;
cur->p++;
}
return 1;
}
static inline int pull_byte(struct cursor *cursor, u8 *c)
{
if (unlikely(cursor->p >= cursor->end))
return 0;
*c = *cursor->p;
cursor->p++;
return 1;
static inline int consume_until_whitespace(struct cursor *cur, int or_end) {
char c;
int consumedAtLeastOne = 0;
while (cur->p < cur->end) {
c = *cur->p;
if (is_whitespace(c))
return consumedAtLeastOne;
cur->p++;
consumedAtLeastOne = 1;
}
return or_end;
}
static inline int parse_byte(struct cursor *cursor, u8 *c)
{
if (unlikely(cursor->p >= cursor->end))
return 0;
static inline int consume_until_non_alphanumeric(struct cursor *cur, int or_end) {
char c;
int consumedAtLeastOne = 0;
*c = *cursor->p;
//cursor->p++;
while (cur->p < cur->end) {
c = *cur->p;
return 1;
if (!is_alphanumeric(c))
return consumedAtLeastOne;
cur->p++;
consumedAtLeastOne = 1;
}
return or_end;
}
static inline int parse_char(struct cursor *cur, char c) {
@@ -147,312 +110,16 @@ static inline int peek_char(struct cursor *cur, int ind) {
return *(cur->p + ind);
}
static inline int cursor_pull_c_str(struct cursor *cursor, const char **str)
{
*str = (const char*)cursor->p;
for (; cursor->p < cursor->end; cursor->p++) {
if (*cursor->p == 0) {
cursor->p++;
return 1;
}
}
return 0;
static inline int pull_byte(struct cursor *cur, u8 *byte) {
if (cur->p >= cur->end)
return 0;
*byte = *cur->p;
cur->p++;
return 1;
}
static inline int cursor_push_byte(struct cursor *cursor, u8 c)
{
if (unlikely(cursor->p + 1 > cursor->end)) {
return 0;
}
*cursor->p = c;
cursor->p++;
return 1;
}
static inline int cursor_pull(struct cursor *cursor, u8 *data, int len)
{
if (unlikely(cursor->p + len > cursor->end)) {
return 0;
}
memcpy(data, cursor->p, len);
cursor->p += len;
return 1;
}
static inline int pull_data_into_cursor(struct cursor *cursor,
struct cursor *dest,
unsigned char **data,
int len)
{
int ok;
if (unlikely(dest->p + len > dest->end)) {
printf("not enough room in dest buffer\n");
return 0;
}
ok = cursor_pull(cursor, dest->p, len);
if (!ok) return 0;
*data = dest->p;
dest->p += len;
return 1;
}
static inline int cursor_dropn(struct cursor *cur, int size, int n)
{
if (n == 0)
return 1;
if (unlikely(cur->p - size*n < cur->start)) {
return 0;
}
cur->p -= size*n;
return 1;
}
static inline int cursor_drop(struct cursor *cur, int size)
{
return cursor_dropn(cur, size, 1);
}
static inline unsigned char *cursor_topn(struct cursor *cur, int len, int n)
{
n += 1;
if (unlikely(cur->p - len*n < cur->start)) {
return NULL;
}
return cur->p - len*n;
}
static inline unsigned char *cursor_top(struct cursor *cur, int len)
{
if (unlikely(cur->p - len < cur->start)) {
return NULL;
}
return cur->p - len;
}
static inline int cursor_top_int(struct cursor *cur, int *i)
{
u8 *p;
if (unlikely(!(p = cursor_top(cur, sizeof(*i))))) {
return 0;
}
*i = *((int*)p);
return 1;
}
static inline int cursor_pop(struct cursor *cur, u8 *data, int len)
{
if (unlikely(cur->p - len < cur->start)) {
return 0;
}
cur->p -= len;
memcpy(data, cur->p, len);
return 1;
}
static inline int cursor_push(struct cursor *cursor, u8 *data, int len)
{
if (unlikely(cursor->p + len >= cursor->end)) {
return 0;
}
if (cursor->p != data)
memcpy(cursor->p, data, len);
cursor->p += len;
return 1;
}
static inline int cursor_push_int(struct cursor *cursor, int i)
{
return cursor_push(cursor, (u8*)&i, sizeof(i));
}
static inline size_t cursor_count(struct cursor *cursor, size_t elem_size)
{
return (cursor->p - cursor->start)/elem_size;
}
/* TODO: push_varint */
static inline int push_varint(struct cursor *cursor, int n)
{
int ok, len;
unsigned char b;
len = 0;
while (1) {
b = (n & 0xFF) | 0x80;
n >>= 7;
if (n == 0) {
b &= 0x7F;
ok = cursor_push_byte(cursor, b);
len++;
if (!ok) return 0;
break;
}
ok = cursor_push_byte(cursor, b);
len++;
if (!ok) return 0;
}
return len;
}
/* TODO: pull_varint */
static inline int pull_varint(struct cursor *cursor, int *n)
{
int ok, i;
unsigned char b;
*n = 0;
for (i = 0;; i++) {
ok = pull_byte(cursor, &b);
if (!ok) return 0;
*n |= ((int)b & 0x7F) << (i * 7);
/* is_last */
if ((b & 0x80) == 0) {
return i+1;
}
if (i == 4) return 0;
}
return 0;
}
static inline int cursor_pull_int(struct cursor *cursor, int *i)
{
return cursor_pull(cursor, (u8*)i, sizeof(*i));
}
static inline int cursor_push_u32(struct cursor *cursor, uint32_t i) {
return cursor_push(cursor, (unsigned char*)&i, sizeof(i));
}
static inline int cursor_push_u16(struct cursor *cursor, u16 i)
{
return cursor_push(cursor, (u8*)&i, sizeof(i));
}
static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size)
{
u8 *p;
p = &cursor->start[elem_size * index];
if (unlikely(p >= cursor->end))
return NULL;
return (void*)p;
}
static inline int push_sized_str(struct cursor *cursor, const char *str, int len)
{
return cursor_push(cursor, (u8*)str, len);
}
static inline int cursor_push_lowercase(struct cursor *cur, const char *str, int len)
{
int i;
if (unlikely(cur->p + len >= cur->end))
return 0;
for (i = 0; i < len; i++)
cur->p[i] = tolower(str[i]);
cur->p += len;
return 1;
}
static inline int cursor_push_str(struct cursor *cursor, const char *str)
{
return cursor_push(cursor, (u8*)str, (int)strlen(str));
}
static inline int cursor_push_c_str(struct cursor *cursor, const char *str)
{
return cursor_push_str(cursor, str) && cursor_push_byte(cursor, 0);
}
/* TODO: push varint size */
static inline int push_prefixed_str(struct cursor *cursor, const char *str)
{
int ok, len;
len = (int)strlen(str);
ok = push_varint(cursor, len);
if (!ok) return 0;
return push_sized_str(cursor, str, len);
}
static inline int pull_prefixed_str(struct cursor *cursor, struct cursor *dest_buf, const char **str)
{
int len, ok;
ok = pull_varint(cursor, &len);
if (!ok) return 0;
if (unlikely(dest_buf->p + len > dest_buf->end)) {
return 0;
}
ok = pull_data_into_cursor(cursor, dest_buf, (unsigned char**)str, len);
if (!ok) return 0;
ok = cursor_push_byte(dest_buf, 0);
return 1;
}
static inline int cursor_remaining_capacity(struct cursor *cursor)
{
return (int)(cursor->end - cursor->p);
}
#define max(a,b) ((a) > (b) ? (a) : (b))
static inline void cursor_print_around(struct cursor *cur, int range)
{
unsigned char *c;
printf("[%ld/%ld]\n", cur->p - cur->start, cur->end - cur->start);
c = max(cur->p - range, cur->start);
for (; c < cur->end && c < (cur->p + range); c++) {
printf("%02x", *c);
}
printf("\n");
c = max(cur->p - range, cur->start);
for (; c < cur->end && c < (cur->p + range); c++) {
if (c == cur->p) {
printf("^");
continue;
}
printf(" ");
}
printf("\n");
}
#undef max
static inline int pull_bytes(struct cursor *cur, int count, const u8 **bytes) {
if (cur->p + count > cur->end)
return 0;
@@ -485,262 +152,5 @@ static inline int parse_str(struct cursor *cur, const char *str) {
return 1;
}
static inline int is_whitespace(int c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}
static inline int next_char_is_whitespace(unsigned char *curChar, unsigned char *endChar) {
unsigned char * next = curChar + 1;
if(next > endChar) return 0;
else if(next == endChar) return 1;
return is_whitespace(*next);
}
static int char_disallowed_at_end_url(char c){
return c == '.' || c == ',';
}
static inline int is_final_url_char(unsigned char *curChar, unsigned char *endChar){
if(is_whitespace(*curChar)){
return 1;
}
else if(next_char_is_whitespace(curChar, endChar)) {
// next char is whitespace so this char could be the final char in the url
return char_disallowed_at_end_url(*curChar);
}
else{
// next char isn't whitespace so it can't be a final char
return 0;
}
}
static inline int is_underscore(int c) {
return c == '_';
}
static inline int is_utf8_byte(u8 c) {
return c & 0x80;
}
static inline int parse_utf8_char(struct cursor *cursor, unsigned int *code_point, unsigned int *utf8_length)
{
u8 first_byte;
if (!parse_byte(cursor, &first_byte))
return 0; // Not enough data
// Determine the number of bytes in this UTF-8 character
int remaining_bytes = 0;
if (first_byte < 0x80) {
*code_point = first_byte;
return 1;
} else if ((first_byte & 0xE0) == 0xC0) {
remaining_bytes = 1;
*utf8_length = remaining_bytes + 1;
*code_point = first_byte & 0x1F;
} else if ((first_byte & 0xF0) == 0xE0) {
remaining_bytes = 2;
*utf8_length = remaining_bytes + 1;
*code_point = first_byte & 0x0F;
} else if ((first_byte & 0xF8) == 0xF0) {
remaining_bytes = 3;
*utf8_length = remaining_bytes + 1;
*code_point = first_byte & 0x07;
} else {
remaining_bytes = 0;
*utf8_length = 1; // Assume 1 byte length for unrecognized UTF-8 characters
// TODO: We need to gracefully handle unrecognized UTF-8 characters
printf("Invalid UTF-8 byte: %x\n", *code_point);
*code_point = ((first_byte & 0xF0) << 6); // Prevent testing as punctuation
return 0; // Invalid first byte
}
// Peek at remaining bytes
for (int i = 0; i < remaining_bytes; ++i) {
signed char next_byte;
if ((next_byte = peek_char(cursor, i+1)) == -1) {
*utf8_length = 1;
return 0; // Not enough data
}
// Debugging lines
//printf("Cursor: %s\n", cursor->p);
//printf("Codepoint: %x\n", *code_point);
//printf("Codepoint <<6: %x\n", ((*code_point << 6) | (next_byte & 0x3F)));
//printf("Remaining bytes: %x\n", remaining_bytes);
//printf("First byte: %x\n", first_byte);
//printf("Next byte: %x\n", next_byte);
//printf("Bitwise AND result: %x\n", (next_byte & 0xC0));
if ((next_byte & 0xC0) != 0x80) {
*utf8_length = 1;
return 0; // Invalid byte in sequence
}
*code_point = (*code_point << 6) | (next_byte & 0x3F);
}
return 1;
}
/**
* Checks if a given Unicode code point is a punctuation character
*
* @param codepoint The Unicode code point to check. @return true if the
* code point is a punctuation character, false otherwise.
*/
static inline int is_punctuation(unsigned int codepoint) {
// Check for underscore (underscore is not treated as punctuation)
if (is_underscore(codepoint))
return 0;
// Check for ASCII punctuation
if (ispunct(codepoint))
return 1;
// Check for Unicode punctuation exceptions (punctuation allowed in hashtags)
if (codepoint == 0x301C || codepoint == 0xFF5E) // Japanese Wave Dash / Tilde
return 0;
// Check for Unicode punctuation
// NOTE: We may need to adjust the codepoint ranges in the future,
// to include/exclude certain types of Unicode characters in hashtags.
// Unicode Blocks Reference: https://www.compart.com/en/unicode/block
return (
// Latin-1 Supplement No-Break Space (NBSP): U+00A0
(codepoint == 0x00A0) ||
// Latin-1 Supplement Punctuation: U+00A1 to U+00BF
(codepoint >= 0x00A1 && codepoint <= 0x00BF) ||
// General Punctuation: U+2000 to U+206F
(codepoint >= 0x2000 && codepoint <= 0x206F) ||
// Currency Symbols: U+20A0 to U+20CF
(codepoint >= 0x20A0 && codepoint <= 0x20CF) ||
// Supplemental Punctuation: U+2E00 to U+2E7F
(codepoint >= 0x2E00 && codepoint <= 0x2E7F) ||
// CJK Symbols and Punctuation: U+3000 to U+303F
(codepoint >= 0x3000 && codepoint <= 0x303F) ||
// Ideographic Description Characters: U+2FF0 to U+2FFF
(codepoint >= 0x2FF0 && codepoint <= 0x2FFF)
);
}
static inline int is_right_boundary(int c) {
return is_whitespace(c) || is_punctuation(c);
}
static inline int is_left_boundary(char c) {
return is_right_boundary(c) || is_utf8_byte(c);
}
static inline int is_alphanumeric(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
}
static inline int consume_until_boundary(struct cursor *cur) {
unsigned int c;
unsigned int char_length = 1;
unsigned int *utf8_char_length = &char_length;
while (cur->p < cur->end) {
c = *cur->p;
*utf8_char_length = 1;
if (is_whitespace(c))
return 1;
// Need to check for UTF-8 characters, which can be multiple bytes long
if (is_utf8_byte(c)) {
if (!parse_utf8_char(cur, &c, utf8_char_length)) {
if (!is_right_boundary(c)){
// TODO: We should work towards handling all UTF-8 characters.
printf("Invalid UTF-8 code point: %x\n", c);
}
}
}
if (is_right_boundary(c))
return 1;
// Need to use a variable character byte length for UTF-8 (2-4 bytes)
if (cur->p + *utf8_char_length <= cur->end)
cur->p += *utf8_char_length;
else
cur->p++;
}
return 1;
}
static inline int consume_until_whitespace(struct cursor *cur, int or_end) {
char c;
int consumedAtLeastOne = 0;
while (cur->p < cur->end) {
c = *cur->p;
if (is_whitespace(c))
return consumedAtLeastOne;
cur->p++;
consumedAtLeastOne = 1;
}
return or_end;
}
static inline int consume_until_end_url(struct cursor *cur, int or_end) {
char c;
int consumedAtLeastOne = 0;
while (cur->p < cur->end) {
c = *cur->p;
if (is_final_url_char(cur->p, cur->end))
return consumedAtLeastOne;
cur->p++;
consumedAtLeastOne = 1;
}
return or_end;
}
static inline int consume_until_non_alphanumeric(struct cursor *cur, int or_end) {
char c;
int consumedAtLeastOne = 0;
while (cur->p < cur->end) {
c = *cur->p;
if (!is_alphanumeric(c))
return consumedAtLeastOne;
cur->p++;
consumedAtLeastOne = 1;
}
return or_end;
}
static inline int cursor_memset(struct cursor *cursor, unsigned char c, int n)
{
if (cursor->p + n >= cursor->end)
return 0;
memset(cursor->p, c, n);
cursor->p += n;
return 1;
}
#endif
#endif /* cursor_h */

View File

@@ -5,9 +5,3 @@
#include "damus.h"
#include "bolt11.h"
#include "amount.h"
#include "nostr_bech32.h"
#include "wasm.h"
#include "nostrscript.h"
#include "nostrdb.h"
#include "lmdb.h"

View File

@@ -28,9 +28,9 @@ static int parse_digit(struct cursor *cur, int *digit) {
}
static int parse_mention_index(struct cursor *cur, struct note_block *block) {
static int parse_mention_index(struct cursor *cur, struct block *block) {
int d1, d2, d3, ind;
u8 *start = cur->p;
const u8 *start = cur->p;
if (!parse_str(cur, "#["))
return 0;
@@ -59,9 +59,9 @@ static int parse_mention_index(struct cursor *cur, struct note_block *block) {
return 1;
}
static int parse_hashtag(struct cursor *cur, struct note_block *block) {
static int parse_hashtag(struct cursor *cur, struct block *block) {
int c;
u8 *start = cur->p;
const u8 *start = cur->p;
if (!parse_char(cur, '#'))
return 0;
@@ -81,7 +81,7 @@ static int parse_hashtag(struct cursor *cur, struct note_block *block) {
return 1;
}
static int add_block(struct note_blocks *blocks, struct note_block block)
static int add_block(struct blocks *blocks, struct block block)
{
if (blocks->num_blocks + 1 >= MAX_BLOCKS)
return 0;
@@ -90,9 +90,9 @@ static int add_block(struct note_blocks *blocks, struct note_block block)
return 1;
}
static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8 *end)
static int add_text_block(struct blocks *blocks, const u8 *start, const u8 *end)
{
struct note_block b;
struct block b;
if (start == end)
return 1;
@@ -104,74 +104,8 @@ static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8
return add_block(blocks, b);
}
static int consume_url_fragment(struct cursor *cur)
{
int c;
if ((c = peek_char(cur, 0)) < 0)
return 1;
if (c != '#' && c != '?') {
return 1;
}
cur->p++;
return consume_until_end_url(cur, 1);
}
static int consume_url_path(struct cursor *cur)
{
int c;
if ((c = peek_char(cur, 0)) < 0)
return 1;
if (c != '/') {
return 1;
}
while (cur->p < cur->end) {
c = *cur->p;
if (c == '?' || c == '#' || is_final_url_char(cur->p, cur->end)) {
return 1;
}
cur->p++;
}
return 1;
}
static int consume_url_host(struct cursor *cur)
{
char c;
int count = 0;
while (cur->p < cur->end) {
c = *cur->p;
// TODO: handle IDNs
if ((is_alphanumeric(c) || c == '.' || c == '-') && !is_final_url_char(cur->p, cur->end))
{
count++;
cur->p++;
continue;
}
return count != 0;
}
// this means the end of the URL hostname is the end of the buffer and we finished
return count != 0;
}
static int parse_url(struct cursor *cur, struct note_block *block) {
u8 *start = cur->p;
u8 *host;
int host_len;
struct cursor path_cur;
static int parse_url(struct cursor *cur, struct block *block) {
const u8 *start = cur->p;
if (!parse_str(cur, "http"))
return 0;
@@ -187,58 +121,15 @@ static int parse_url(struct cursor *cur, struct note_block *block) {
return 0;
}
}
// make sure to save the hostname. We will use this to detect damus.io links
host = cur->p;
if (!consume_url_host(cur)) {
cur->p = start;
return 0;
if (!consume_until_whitespace(cur, 1)) {
cur->p = start;
return 0;
}
// get the length of the host string
host_len = (int)(cur->p - host);
// save the current parse state so that we can continue from here when
// parsing the bech32 in the damus.io link if we have it
copy_cursor(cur, &path_cur);
// skip leading /
cursor_skip(&path_cur, 1);
if (!consume_url_path(cur)) {
cur->p = start;
return 0;
}
if (!consume_url_fragment(cur)) {
cur->p = start;
return 0;
}
// smart parens
if (start - 1 >= 0 &&
start < cur->end &&
*(start - 1) == '(' &&
(cur->p - 1) < cur->end &&
*(cur->p - 1) == ')')
{
cur->p--;
}
// save the bech32 string pos in case we hit a damus.io link
block->block.str.start = (const char *)path_cur.p;
// if we have a damus link, make it a mention
if (host_len == 8
&& !strncmp((const char *)host, "damus.io", 8)
&& parse_nostr_bech32(&path_cur, &block->block.mention_bech32.bech32))
{
block->block.str.end = (const char *)path_cur.p;
block->type = BLOCK_MENTION_BECH32;
return 1;
}
// strip any unwanted characters
while(is_invalid_url_ending(peek_char(cur, -1))) cur->p--;
block->type = BLOCK_URL;
block->block.str.start = (const char *)start;
block->block.str.end = (const char *)cur->p;
@@ -246,8 +137,8 @@ static int parse_url(struct cursor *cur, struct note_block *block) {
return 1;
}
static int parse_invoice(struct cursor *cur, struct note_block *block) {
u8 *start, *end;
static int parse_invoice(struct cursor *cur, struct block *block) {
const u8 *start, *end;
char *fail;
struct bolt11 *bolt11;
// optional
@@ -286,12 +177,12 @@ static int parse_invoice(struct cursor *cur, struct note_block *block) {
}
static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
u8 *start = cur->p;
static int parse_mention_bech32(struct cursor *cur, struct block *block) {
const u8 *start = cur->p;
if (!parse_str(cur, "nostr:"))
return 0;
parse_char(cur, '@');
parse_str(cur, "nostr:");
block->block.str.start = (const char *)cur->p;
if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) {
@@ -306,7 +197,7 @@ static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
return 1;
}
static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, struct note_block block, u8 **start, const u8 *pre_mention)
static int add_text_then_block(struct cursor *cur, struct blocks *blocks, struct block block, const u8 **start, const u8 *pre_mention)
{
if (!add_text_block(blocks, *start, pre_mention))
return 0;
@@ -319,28 +210,22 @@ static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, s
return 1;
}
int damus_parse_content(struct note_blocks *blocks, const char *content) {
int damus_parse_content(struct blocks *blocks, const char *content) {
int cp, c;
struct cursor cur;
struct note_block block;
u8 *start, *pre_mention;
struct block block;
const u8 *start, *pre_mention;
blocks->words = 0;
blocks->num_blocks = 0;
make_cursor((u8*)content, (u8*)content + strlen(content), &cur);
make_cursor(&cur, (const u8*)content, strlen(content));
start = cur.p;
while (cur.p < cur.end && blocks->num_blocks < MAX_BLOCKS) {
cp = peek_char(&cur, -1);
c = peek_char(&cur, 0);
// new word
if (is_whitespace(cp) && !is_whitespace(c)) {
blocks->words++;
}
pre_mention = cur.p;
if (cp == -1 || is_left_boundary(cp) || c == '#') {
if (cp == -1 || is_whitespace(cp) || c == '#') {
if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) {
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
return 0;
@@ -353,7 +238,7 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) {
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
return 0;
continue;
} else if ((c == 'n' || c == '@') && parse_mention_bech32(&cur, &block)) {
} else if (c == 'n' && parse_mention_bech32(&cur, &block)) {
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
return 0;
continue;
@@ -371,12 +256,12 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) {
return 1;
}
void blocks_init(struct note_blocks *blocks) {
blocks->blocks = malloc(sizeof(struct note_block) * MAX_BLOCKS);
void blocks_init(struct blocks *blocks) {
blocks->blocks = malloc(sizeof(struct block) * MAX_BLOCKS);
blocks->num_blocks = 0;
}
void blocks_free(struct note_blocks *blocks) {
void blocks_free(struct blocks *blocks) {
if (!blocks->blocks) {
return;
}

View File

@@ -9,10 +9,10 @@
#define damus_h
#include <stdio.h>
#include "nostr_bech32.h"
#include "block.h"
typedef unsigned char u8;
int damus_parse_content(struct note_blocks *blocks, const char *content);
int damus_parse_content(struct blocks *blocks, const char *content);
#endif /* damus_h */

View File

@@ -1,15 +0,0 @@
#ifndef PROTOVERSE_DEBUG_H
#define PROTOVERSE_DEBUG_H
#include <stdio.h>
#define unusual(...) fprintf(stderr, "UNUSUAL: " __VA_ARGS__)
#ifdef DEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
#endif /* PROTOVERSE_DEBUG_H */

View File

@@ -1,34 +0,0 @@
#include "error.h"
#include <stdlib.h>
#include <stdarg.h>
int note_error_(struct errors *errs_, struct cursor *p, const char *fmt, ...)
{
static char buf[512];
struct error err;
struct cursor *errs;
va_list ap;
errs = &errs_->cur;
if (errs_->enabled == 0)
return 0;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
err.msg = buf;
err.pos = p ? (int)(p->p - p->start) : 0;
if (!cursor_push_error(errs, &err)) {
fprintf(stderr, "arena OOM when recording error, ");
fprintf(stderr, "errs->p at %ld, remaining %ld, strlen %ld\n",
errs->p - errs->start, errs->end - errs->p, strlen(buf));
}
return 0;
}

View File

@@ -1,33 +0,0 @@
#ifndef PROTOVERSE_ERROR_H
#define PROTOVERSE_ERROR_H
#include "cursor.h"
struct error {
int pos;
const char *msg;
};
struct errors {
struct cursor cur;
int enabled;
};
#define note_error(errs, p, fmt, ...) note_error_(errs, p, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
static inline int cursor_push_error(struct cursor *cur, struct error *err)
{
return cursor_push_int(cur, err->pos) &&
cursor_push_c_str(cur, err->msg);
}
static inline int cursor_pull_error(struct cursor *cur, struct error *err)
{
return cursor_pull_int(cur, &err->pos) &&
cursor_pull_c_str(cur, &err->msg);
}
int note_error_(struct errors *errs, struct cursor *p, const char *fmt, ...);
#endif /* PROTOVERSE_ERROR_H */

View File

@@ -39,6 +39,15 @@ bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize)
return slen == 0 && bufsize == 0;
}
static char hexchar(unsigned int val)
{
if (val < 10)
return '0' + val;
if (val < 16)
return 'a' + val - 10;
abort();
}
bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
{
size_t i;

View File

@@ -70,15 +70,4 @@ static inline size_t hex_data_size(size_t strlen)
{
return strlen / 2;
}
static inline char hexchar(unsigned int val)
{
if (val < 10)
return '0' + val;
if (val < 16)
return 'a' + val - 10;
abort();
}
#endif /* CCAN_HEX_H */

View File

@@ -52,13 +52,9 @@
*/
#define unlikely(cond) __builtin_expect(!!(cond), 0)
#else
#ifndef likely
#define likely(cond) (!!(cond))
#endif
#ifndef unlikely
#define unlikely(cond) (!!(cond))
#endif
#endif
#else /* CCAN_LIKELY_DEBUG versions */
#include <ccan/str/str.h>

View File

@@ -7,10 +7,8 @@
#include "nostr_bech32.h"
#include <stdlib.h>
#include "endian.h"
#include "cursor.h"
#include "bech32.h"
#include <stdbool.h>
#define MAX_TLVS 16
@@ -93,9 +91,6 @@ static int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *t
} else if (strcmp(prefix, "npub") == 0) {
*type = NOSTR_BECH32_NPUB;
return 1;
} else if (strcmp(prefix, "nsec") == 0) {
*type = NOSTR_BECH32_NSEC;
return 1;
} else if (strcmp(prefix, "nprofile") == 0) {
*type = NOSTR_BECH32_NPROFILE;
return 1;
@@ -121,10 +116,6 @@ static int parse_nostr_bech32_npub(struct cursor *cur, struct bech32_npub *npub)
return pull_bytes(cur, 32, &npub->pubkey);
}
static int parse_nostr_bech32_nsec(struct cursor *cur, struct bech32_nsec *nsec) {
return pull_bytes(cur, 32, &nsec->nsec);
}
static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
struct nostr_tlv *tlv;
struct str_block *str;
@@ -147,11 +138,6 @@ static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
return 1;
}
static uint32_t decode_tlv_u32(const uint8_t *bytes) {
beint32_t *be32_bytes = (beint32_t*)bytes;
return be32_to_cpu(*be32_bytes);
}
static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *nevent) {
struct nostr_tlvs tlvs;
struct nostr_tlv *tlv;
@@ -173,13 +159,6 @@ static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *n
nevent->pubkey = NULL;
}
if(find_tlv(&tlvs, TLV_KIND, &tlv)) {
nevent->kind = decode_tlv_u32(tlv->value);
nevent->has_kind = true;
} else {
nevent->has_kind = false;
}
return tlvs_to_relays(&tlvs, &nevent->relays);
}
@@ -201,11 +180,6 @@ static int parse_nostr_bech32_naddr(struct cursor *cur, struct bech32_naddr *nad
naddr->pubkey = tlv->value;
if(!find_tlv(&tlvs, TLV_KIND, &tlv)) {
return 0;
}
naddr->kind = decode_tlv_u32(tlv->value);
return tlvs_to_relays(&tlvs, &naddr->relays);
}
@@ -244,7 +218,7 @@ static int parse_nostr_bech32_nrelay(struct cursor *cur, struct bech32_nrelay *n
}
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
u8 *start, *end;
const u8 *start, *end;
start = cur->p;
@@ -283,7 +257,7 @@ int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
}
struct cursor bcur;
make_cursor(obj->buffer, obj->buffer + obj->buflen, &bcur);
make_cursor(&bcur, obj->buffer, obj->buflen);
switch (obj->type) {
case NOSTR_BECH32_NOTE:
@@ -294,10 +268,6 @@ int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub))
goto fail;
break;
case NOSTR_BECH32_NSEC:
if (!parse_nostr_bech32_nsec(&bcur, &obj->data.nsec))
goto fail;
break;
case NOSTR_BECH32_NEVENT:
if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent))
goto fail;

View File

@@ -11,8 +11,6 @@
#include <stdio.h>
#include "str_block.h"
#include "cursor.h"
#include <stdbool.h>
typedef unsigned char u8;
#define MAX_RELAYS 10
@@ -28,7 +26,6 @@ enum nostr_bech32_type {
NOSTR_BECH32_NEVENT = 4,
NOSTR_BECH32_NRELAY = 5,
NOSTR_BECH32_NADDR = 6,
NOSTR_BECH32_NSEC = 7,
};
struct bech32_note {
@@ -39,16 +36,10 @@ struct bech32_npub {
const u8 *pubkey;
};
struct bech32_nsec {
const u8 *nsec;
};
struct bech32_nevent {
struct relays relays;
const u8 *event_id;
const u8 *pubkey; // optional
uint32_t kind;
bool has_kind;
};
struct bech32_nprofile {
@@ -60,7 +51,6 @@ struct bech32_naddr {
struct relays relays;
struct str_block identifier;
const u8 *pubkey;
uint32_t kind;
};
struct bech32_nrelay {
@@ -75,7 +65,6 @@ typedef struct nostr_bech32 {
union {
struct bech32_note note;
struct bech32_npub npub;
struct bech32_nsec nsec;
struct bech32_nevent nevent;
struct bech32_nprofile nprofile;
struct bech32_naddr naddr;

View File

@@ -1,42 +0,0 @@
#ifndef CURSOR_PARSER
#define CURSOR_PARSER
#include "cursor.h"
static int consume_bytes(struct cursor *cursor, const unsigned char *match, int len)
{
int i;
if (cursor->p + len > cursor->end) {
fprintf(stderr, "consume_bytes overflow\n");
return 0;
}
for (i = 0; i < len; i++) {
if (cursor->p[i] != match[i])
return 0;
}
cursor->p += len;
return 1;
}
static inline int consume_byte(struct cursor *cursor, unsigned char match)
{
if (unlikely(cursor->p >= cursor->end))
return 0;
if (*cursor->p != match)
return 0;
cursor->p++;
return 1;
}
static inline int consume_u32(struct cursor *cursor, unsigned int match)
{
return consume_bytes(cursor, (unsigned char*)&match, sizeof(match));
}
#endif /* CURSOR_PARSER */

View File

@@ -1,14 +0,0 @@
#ifndef PROTOVERSE_TYPEDEFS_H
#define PROTOVERSE_TYPEDEFS_H
#include <stdint.h>
typedef unsigned char u8;
typedef unsigned int u32;
typedef unsigned short u16;
typedef uint64_t u64;
typedef int64_t s64;
#endif /* PROTOVERSE_TYPEDEFS_H */

View File

@@ -1,14 +0,0 @@
#ifndef PROTOVERSE_VARINT_H
#define PROTOVERSE_VARINT_H
#define VARINT_MAX_LEN 9
#include <stddef.h>
#include <stdint.h>
size_t varint_put(unsigned char buf[VARINT_MAX_LEN], uint64_t v);
size_t varint_size(uint64_t v);
size_t varint_get(const unsigned char *p, size_t max, int64_t *val);
#endif /* PROTOVERSE_VARINT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,850 +0,0 @@
#ifndef PROTOVERSE_WASM_H
#define PROTOVERSE_WASM_H
static const unsigned char WASM_MAGIC[] = {0,'a','s','m'};
#define WASM_VERSION 0x01
#define MAX_U32_LEB128_BYTES 5
#define MAX_U64_LEB128_BYTES 10
#define MAX_CUSTOM_SECTIONS 32
#define MAX_BUILTINS 64
#define BUILTIN_SUSPEND 42
#define FUNC_TYPE_TAG 0x60
#include "cursor.h"
#include "error.h"
#ifdef NOINLINE
#define INLINE __attribute__((noinline))
#else
#define INLINE inline
#endif
#define interp_error(p, fmt, ...) note_error(&((p)->errors), interp_codeptr(p), fmt, ##__VA_ARGS__)
#define parse_err(p, fmt, ...) note_error(&((p)->errs), &(p)->cur, fmt, ##__VA_ARGS__)
enum valtype {
val_i32 = 0x7F,
val_i64 = 0x7E,
val_f32 = 0x7D,
val_f64 = 0x7C,
val_ref_null = 0xD0,
val_ref_func = 0x70,
val_ref_extern = 0x6F,
};
enum const_instr {
ci_const_i32 = 0x41,
ci_const_i64 = 0x42,
ci_const_f32 = 0x43,
ci_const_f64 = 0x44,
ci_ref_null = 0xD0,
ci_ref_func = 0xD2,
ci_global_get = 0x23,
ci_end = 0x0B,
};
enum limit_type {
limit_min = 0x00,
limit_min_max = 0x01,
};
struct limits {
u32 min;
u32 max;
enum limit_type type;
};
enum section_tag {
section_custom,
section_type,
section_import,
section_function,
section_table,
section_memory,
section_global,
section_export,
section_start,
section_element,
section_code,
section_data,
section_data_count,
section_name,
num_sections,
};
enum name_subsection_tag {
name_subsection_module,
name_subsection_funcs,
name_subsection_locals,
num_name_subsections,
};
enum reftype {
funcref = 0x70,
externref = 0x6F,
};
struct resulttype {
unsigned char *valtypes; /* enum valtype */
u32 num_valtypes;
};
struct functype {
struct resulttype params;
struct resulttype result;
};
struct table {
enum reftype reftype;
struct limits limits;
};
struct tablesec {
struct table *tables;
u32 num_tables;
};
enum elem_mode {
elem_mode_passive,
elem_mode_active,
elem_mode_declarative,
};
struct expr {
u8 *code;
u32 code_len;
};
struct refval {
u32 addr;
};
struct table_inst {
struct refval *refs;
enum reftype reftype;
u32 num_refs;
};
struct numval {
union {
int i32;
u32 u32;
int64_t i64;
uint64_t u64;
float f32;
double f64;
};
};
struct val {
enum valtype type;
union {
struct numval num;
struct refval ref;
};
};
struct elem_inst {
struct val val;
u16 elem;
u16 init;
};
struct elem {
struct expr offset;
u32 tableidx;
struct expr *inits;
u32 num_inits;
enum elem_mode mode;
enum reftype reftype;
struct val val;
};
struct customsec {
const char *name;
unsigned char *data;
u32 data_len;
};
struct elemsec {
struct elem *elements;
u32 num_elements;
};
struct memsec {
struct limits *mems; /* memtype */
u32 num_mems;
};
struct funcsec {
u32 *type_indices;
u32 num_indices;
};
enum mut {
mut_const,
mut_var,
};
struct globaltype {
enum valtype valtype;
enum mut mut;
};
struct globalsec {
struct global *globals;
u32 num_globals;
};
struct typesec {
struct functype *functypes;
u32 num_functypes;
};
enum import_type {
import_func,
import_table,
import_mem,
import_global,
};
struct importdesc {
enum import_type type;
union {
u32 typeidx;
struct limits tabletype;
struct limits memtype;
struct globaltype globaltype;
};
};
struct import {
const char *module_name;
const char *name;
struct importdesc desc;
int resolved_builtin;
};
struct importsec {
struct import *imports;
u32 num_imports;
};
struct global {
struct globaltype type;
struct expr init;
struct val val;
};
struct local_def {
u32 num_types;
enum valtype type;
};
/* "code" */
struct wasm_func {
struct expr code;
struct local_def *local_defs;
u32 num_local_defs;
};
enum func_type {
func_type_wasm,
func_type_builtin,
};
struct func {
union {
struct wasm_func *wasm_func;
struct builtin *builtin;
};
u32 num_locals;
struct functype *functype;
enum func_type type;
const char *name;
u32 idx;
};
struct codesec {
struct wasm_func *funcs;
u32 num_funcs;
};
enum exportdesc {
export_func,
export_table,
export_mem,
export_global,
};
struct wexport {
const char *name;
u32 index;
enum exportdesc desc;
};
struct exportsec {
struct wexport *exports;
u32 num_exports;
};
struct nameassoc {
u32 index;
const char *name;
};
struct namemap {
struct nameassoc *names;
u32 num_names;
};
struct namesec {
const char *module_name;
struct namemap func_names;
int parsed;
};
struct wsection {
enum section_tag tag;
};
enum bulk_tag {
i_memory_copy = 10,
i_memory_fill = 11,
i_table_init = 12,
i_elem_drop = 13,
i_table_copy = 14,
i_table_grow = 15,
i_table_size = 16,
i_table_fill = 17,
};
enum instr_tag {
/* control instructions */
i_unreachable = 0x00,
i_nop = 0x01,
i_block = 0x02,
i_loop = 0x03,
i_if = 0x04,
i_else = 0x05,
i_end = 0x0B,
i_br = 0x0C,
i_br_if = 0x0D,
i_br_table = 0x0E,
i_return = 0x0F,
i_call = 0x10,
i_call_indirect = 0x11,
/* parametric instructions */
i_drop = 0x1A,
i_select = 0x1B,
i_selects = 0x1C,
/* variable instructions */
i_local_get = 0x20,
i_local_set = 0x21,
i_local_tee = 0x22,
i_global_get = 0x23,
i_global_set = 0x24,
i_table_get = 0x25,
i_table_set = 0x26,
/* memory instructions */
i_i32_load = 0x28,
i_i64_load = 0x29,
i_f32_load = 0x2A,
i_f64_load = 0x2B,
i_i32_load8_s = 0x2C,
i_i32_load8_u = 0x2D,
i_i32_load16_s = 0x2E,
i_i32_load16_u = 0x2F,
i_i64_load8_s = 0x30,
i_i64_load8_u = 0x31,
i_i64_load16_s = 0x32,
i_i64_load16_u = 0x33,
i_i64_load32_s = 0x34,
i_i64_load32_u = 0x35,
i_i32_store = 0x36,
i_i64_store = 0x37,
i_f32_store = 0x38,
i_f64_store = 0x39,
i_i32_store8 = 0x3A,
i_i32_store16 = 0x3B,
i_i64_store8 = 0x3C,
i_i64_store16 = 0x3D,
i_i64_store32 = 0x3E,
i_memory_size = 0x3F,
i_memory_grow = 0x40,
/* numeric instructions */
i_i32_const = 0x41,
i_i64_const = 0x42,
i_f32_const = 0x43,
i_f64_const = 0x44,
i_i32_eqz = 0x45,
i_i32_eq = 0x46,
i_i32_ne = 0x47,
i_i32_lt_s = 0x48,
i_i32_lt_u = 0x49,
i_i32_gt_s = 0x4A,
i_i32_gt_u = 0x4B,
i_i32_le_s = 0x4C,
i_i32_le_u = 0x4D,
i_i32_ge_s = 0x4E,
i_i32_ge_u = 0x4F,
i_i64_eqz = 0x50,
i_i64_eq = 0x51,
i_i64_ne = 0x52,
i_i64_lt_s = 0x53,
i_i64_lt_u = 0x54,
i_i64_gt_s = 0x55,
i_i64_gt_u = 0x56,
i_i64_le_s = 0x57,
i_i64_le_u = 0x58,
i_i64_ge_s = 0x59,
i_i64_ge_u = 0x5A,
i_f32_eq = 0x5B,
i_f32_ne = 0x5C,
i_f32_lt = 0x5D,
i_f32_gt = 0x5E,
i_f32_le = 0x5F,
i_f32_ge = 0x60,
i_f64_eq = 0x61,
i_f64_ne = 0x62,
i_f64_lt = 0x63,
i_f64_gt = 0x64,
i_f64_le = 0x65,
i_f64_ge = 0x66,
i_i32_clz = 0x67,
i_i32_ctz = 0x68,
i_i32_popcnt = 0x69,
i_i32_add = 0x6A,
i_i32_sub = 0x6B,
i_i32_mul = 0x6C,
i_i32_div_s = 0x6D,
i_i32_div_u = 0x6E,
i_i32_rem_s = 0x6F,
i_i32_rem_u = 0x70,
i_i32_and = 0x71,
i_i32_or = 0x72,
i_i32_xor = 0x73,
i_i32_shl = 0x74,
i_i32_shr_s = 0x75,
i_i32_shr_u = 0x76,
i_i32_rotl = 0x77,
i_i32_rotr = 0x78,
i_i64_clz = 0x79,
i_i64_ctz = 0x7A,
i_i64_popcnt = 0x7B,
i_i64_add = 0x7C,
i_i64_sub = 0x7D,
i_i64_mul = 0x7E,
i_i64_div_s = 0x7F,
i_i64_div_u = 0x80,
i_i64_rem_s = 0x81,
i_i64_rem_u = 0x82,
i_i64_and = 0x83,
i_i64_or = 0x84,
i_i64_xor = 0x85,
i_i64_shl = 0x86,
i_i64_shr_s = 0x87,
i_i64_shr_u = 0x88,
i_i64_rotl = 0x89,
i_i64_rotr = 0x8A,
i_f32_abs = 0x8b,
i_f32_neg = 0x8c,
i_f32_ceil = 0x8d,
i_f32_floor = 0x8e,
i_f32_trunc = 0x8f,
i_f32_nearest = 0x90,
i_f32_sqrt = 0x91,
i_f32_add = 0x92,
i_f32_sub = 0x93,
i_f32_mul = 0x94,
i_f32_div = 0x95,
i_f32_min = 0x96,
i_f32_max = 0x97,
i_f32_copysign = 0x98,
i_f64_abs = 0x99,
i_f64_neg = 0x9a,
i_f64_ceil = 0x9b,
i_f64_floor = 0x9c,
i_f64_trunc = 0x9d,
i_f64_nearest = 0x9e,
i_f64_sqrt = 0x9f,
i_f64_add = 0xa0,
i_f64_sub = 0xa1,
i_f64_mul = 0xa2,
i_f64_div = 0xa3,
i_f64_min = 0xa4,
i_f64_max = 0xa5,
i_f64_copysign = 0xa6,
i_i32_wrap_i64 = 0xa7,
i_i32_trunc_f32_s = 0xa8,
i_i32_trunc_f32_u = 0xa9,
i_i32_trunc_f64_s = 0xaa,
i_i32_trunc_f64_u = 0xab,
i_i64_extend_i32_s = 0xac,
i_i64_extend_i32_u = 0xad,
i_i64_trunc_f32_s = 0xae,
i_i64_trunc_f32_u = 0xaf,
i_i64_trunc_f64_s = 0xb0,
i_i64_trunc_f64_u = 0xb1,
i_f32_convert_i32_s = 0xb2,
i_f32_convert_i32_u = 0xb3,
i_f32_convert_i64_s = 0xb4,
i_f32_convert_i64_u = 0xb5,
i_f32_demote_f64 = 0xb6,
i_f64_convert_i32_s = 0xb7,
i_f64_convert_i32_u = 0xb8,
i_f64_convert_i64_s = 0xb9,
i_f64_convert_i64_u = 0xba,
i_f64_promote_f32 = 0xbb,
i_i32_reinterpret_f32 = 0xbc,
i_i64_reinterpret_f64 = 0xbd,
i_f32_reinterpret_i32 = 0xbe,
i_f64_reinterpret_i64 = 0xbf,
i_i32_extend8_s = 0xc0,
i_i32_extend16_s = 0xc1,
i_i64_extend8_s = 0xc2,
i_i64_extend16_s = 0xc3,
i_i64_extend32_s = 0xc4,
i_ref_null = 0xD0,
i_ref_is_null = 0xD1,
i_ref_func = 0xD2,
i_bulk_op = 0xFC,
/* TODO: more instrs */
};
enum blocktype_tag {
blocktype_empty,
blocktype_valtype,
blocktype_index,
};
struct blocktype {
enum blocktype_tag tag;
union {
enum valtype valtype;
int type_index;
};
};
struct instrs {
unsigned char *data;
u32 len;
};
struct block {
struct blocktype type;
struct expr instrs;
};
struct memarg {
u32 offset;
u32 align;
};
struct br_table {
u32 num_label_indices;
u32 label_indices[512];
u32 default_label;
};
struct call_indirect {
u32 tableidx;
u32 typeidx;
};
struct table_init {
u32 tableidx;
u32 elemidx;
};
struct table_copy {
u32 from;
u32 to;
};
struct bulk_op {
enum bulk_tag tag;
union {
struct table_init table_init;
struct table_copy table_copy;
u32 idx;
};
};
struct select_instr {
u8 *valtypes;
u32 num_valtypes;
};
struct instr {
enum instr_tag tag;
int pos;
union {
struct br_table br_table;
struct bulk_op bulk_op;
struct call_indirect call_indirect;
struct memarg memarg;
struct select_instr select;
struct block block;
struct expr else_block;
double f64;
float f32;
int i32;
u32 u32;
int64_t i64;
u64 u64;
unsigned char memidx;
enum reftype reftype;
};
};
enum datamode {
datamode_active,
datamode_passive,
};
struct wdata_active {
u32 mem_index;
struct expr offset_expr;
};
struct wdata {
struct wdata_active active;
u8 *bytes;
u32 bytes_len;
enum datamode mode;
};
struct datasec {
struct wdata *datas;
u32 num_datas;
};
struct startsec {
u32 start_fn;
};
struct module {
unsigned int parsed;
unsigned int custom_sections;
struct func *funcs;
u32 num_funcs;
struct customsec custom_section[MAX_CUSTOM_SECTIONS];
struct typesec type_section;
struct funcsec func_section;
struct importsec import_section;
struct exportsec export_section;
struct codesec code_section;
struct tablesec table_section;
struct memsec memory_section;
struct globalsec global_section;
struct startsec start_section;
struct elemsec element_section;
struct datasec data_section;
struct namesec name_section;
};
// make sure the struct is packed so that
struct label {
u32 instr_pos; // resolved status is stored in HOB of pos
u32 jump;
};
struct callframe {
struct cursor code;
struct val *locals;
struct func *func;
u16 prev_stack_items;
};
struct resolver {
u16 label;
u8 end_tag;
u8 start_tag;
};
struct global_inst {
struct val val;
};
struct module_inst {
struct table_inst *tables;
struct global_inst *globals;
struct elem_inst *elements;
u32 num_tables;
u32 num_globals;
u32 num_elements;
int start_fn;
unsigned char *globals_init;
};
struct wasi {
int argc;
const char **argv;
int environc;
const char **environ;
};
struct wasm_interp;
struct builtin {
const char *name;
int (*fn)(struct wasm_interp *);
int (*prepare_args)(struct wasm_interp *);
};
struct wasm_interp {
struct module *module;
struct module_inst module_inst;
struct wasi wasi;
void *context;
struct builtin builtins[MAX_BUILTINS];
int num_builtins;
int prev_resolvers, quitting;
struct errors errors; /* struct error */
size_t ops;
struct cursor callframes; /* struct callframe */
struct cursor stack; /* struct val */
struct cursor mem; /* u8/mixed */
struct cursor memory; /* memory pages (65536 blocks) */
struct cursor locals; /* struct val */
struct cursor labels; /* struct labels */
struct cursor num_labels;
// resolve stack for the current function. every time a control
// instruction is encountered, the label index is pushed. When an
// instruction is popped, we can resolve the label
struct cursor resolver_stack; /* struct resolver */
struct cursor resolver_offsets; /* int */
};
struct wasm_parser {
struct module module;
struct builtin *builtins;
u32 num_builtins;
struct cursor cur;
struct cursor mem;
struct errors errs;
};
int run_wasm(unsigned char *wasm, unsigned long len, int argc, const char **argv, char **env, int *retval);
int parse_wasm(struct wasm_parser *p);
int wasm_interp_init(struct wasm_interp *interp, struct module *module);
void wasm_parser_free(struct wasm_parser *parser);
void wasm_parser_init(struct wasm_parser *p, u8 *wasm, size_t wasm_len, size_t arena_size, struct builtin *, int num_builtins);
void wasm_interp_free(struct wasm_interp *interp);
int interp_wasm_module(struct wasm_interp *interp, int *retval);
int interp_wasm_module_resume(struct wasm_interp *interp, int *retval);
void print_error_backtrace(struct errors *errors);
void setup_wasi(struct wasm_interp *interp, int argc, const char **argv, char **env);
void print_callstack(struct wasm_interp *interp);
// builtin helpers
int get_params(struct wasm_interp *interp, struct val** vals, u32 num_vals);
int get_var_params(struct wasm_interp *interp, struct val** vals, u32 *num_vals);
u8 *interp_mem_ptr(struct wasm_interp *interp, u32 ptr, int size);
static INLINE struct callframe *top_callframe(struct cursor *cur)
{
return (struct callframe*)cursor_top(cur, sizeof(struct callframe));
}
static INLINE struct cursor *interp_codeptr(struct wasm_interp *interp)
{
struct callframe *frame;
if (unlikely(!(frame = top_callframe(&interp->callframes))))
return 0;
return &frame->code;
}
static INLINE int mem_ptr_str(struct wasm_interp *interp, u32 ptr,
const char **str)
{
// still technically unsafe if the string runs over the end of memory...
if (!(*str = (const char*)interp_mem_ptr(interp, ptr, 1))) {
return interp_error(interp, "int memptr");
}
return 1;
}
static INLINE int mem_ptr_i32(struct wasm_interp *interp, u32 ptr, int **i)
{
if (!(*i = (int*)interp_mem_ptr(interp, ptr, sizeof(int))))
return interp_error(interp, "int memptr");
return 1;
}
static INLINE int cursor_pushval(struct cursor *cur, struct val *val)
{
return cursor_push(cur, (u8*)val, sizeof(*val));
}
static INLINE int cursor_push_i32(struct cursor *stack, int i)
{
struct val val;
val.type = val_i32;
val.num.i32 = i;
return cursor_pushval(stack, &val);
}
static INLINE int stack_push_i32(struct wasm_interp *interp, int i)
{
return cursor_push_i32(&interp->stack, i);
}
static INLINE struct callframe *top_callframes(struct cursor *cur, int top)
{
return (struct callframe*)cursor_topn(cur, sizeof(struct callframe), top);
}
static INLINE int was_section_parsed(struct module *module,
enum section_tag section)
{
if (section == section_custom)
return module->custom_sections > 0;
return module->parsed & (1 << section);
}
#endif /* PROTOVERSE_WASM_H */

File diff suppressed because it is too large Load Diff

View File

@@ -18,15 +18,6 @@
"version" : "7.6.1"
}
},
{
"identity" : "mcemojipicker",
"kind" : "remoteSourceControl",
"location" : "https://github.com/izyumkin/MCEmojiPicker",
"state" : {
"revision" : "e0b4903b75ae1cc418d276d84d1cb946b8a1d73c",
"version" : "1.2.3"
}
},
{
"identity" : "secp256k1.swift",
"kind" : "remoteSourceControl",
@@ -34,32 +25,6 @@
"state" : {
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
}
},
{
"identity" : "swift-markdown-ui",
"kind" : "remoteSourceControl",
"location" : "https://github.com/damus-io/swift-markdown-ui",
"state" : {
"revision" : "76bb7971da7fbf429de1c84f1244adf657242fee"
}
},
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "5b356adceabff6ca027f6574aac79e9fee145d26",
"version" : "1.14.1"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "74203046135342e4a4a627476dd6caf8b28fe11b",
"version" : "509.0.0"
}
}
],
"version" : 2

View File

@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D79C4C132AFEB061003A41B4"
BuildableName = "DamusNotificationService.appex"
BlueprintName = "DamusNotificationService"
ReferencedContainer = "container:damus.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
BuildableName = "damus.app"
BlueprintName = "damus"
ReferencedContainer = "container:damus.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<RemoteRunnable
runnableDebuggingMode = "1"
BundleIdentifier = "com.jb55.damus2"
RemotePath = "/Users/danielnogueira/Library/Developer/CoreSimulator/Devices/99E60B35-CE5D-4B45-AC35-00818C0AF3CB/data/Containers/Bundle/Application/7D0A5302-D07E-4C7C-B509-A7C552BD5A65/damus.app">
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
BuildableName = "damus.app"
BlueprintName = "damus"
ReferencedContainer = "container:damus.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4CE6DEE227F7A08100C66700"
BuildableName = "damus.app"
BlueprintName = "damus"
ReferencedContainer = "container:damus.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,8 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -71,9 +70,6 @@
ReferencedContainer = "container:damus.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<StoreKitConfigurationFileReference
identifier = "../../Purple.storekit">
</StoreKitConfigurationFileReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@@ -1,20 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1A",
"green" : "0x93",
"red" : "0xF7"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x00",
"green" : "0x00",
"red" : "0x00"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE3",
"green" : "0xD7",
"red" : "0xF7"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x20",
"green" : "0x13",
"red" : "0x61"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x63",
"green" : "0x11",
"red" : "0xF5"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x6E",
"green" : "0x20",
"red" : "0xF8"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xEE",
"green" : "0xE8",
"red" : "0xF7"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x35",
"green" : "0x04",
"red" : "0x8B"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x3D",
"green" : "0x07",
"red" : "0x9C"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x44",
"green" : "0x06",
"red" : "0xB2"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2D",
"green" : "0x05",
"red" : "0x75"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD8",
"green" : "0xC2",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFA",
"green" : "0xFA",
"red" : "0xF9"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x24",
"green" : "0x22",
"red" : "0x20"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE3",
"green" : "0xE1",
"red" : "0xDD"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2A",
"green" : "0x26",
"red" : "0x23"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x59",
"green" : "0x53",
"red" : "0x4A"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x85",
"green" : "0x7A",
"red" : "0x6A"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE4",
"green" : "0xF1",
"red" : "0xD6"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x38",
"green" : "0x5C",
"red" : "0x12"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x5A",
"green" : "0xAB",
"red" : "0x04"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x64",
"green" : "0xBF",
"red" : "0x03"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF0",
"green" : "0xF7",
"red" : "0xE8"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1F",
"green" : "0x33",
"red" : "0x0A"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x34",
"green" : "0x64",
"red" : "0x02"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x3F",
"green" : "0x79",
"red" : "0x02"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1F",
"green" : "0x3C",
"red" : "0x01"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE4",
"green" : "0xFF",
"red" : "0xAD"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD1",
"green" : "0xEE",
"red" : "0xFE"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x12",
"green" : "0x43",
"red" : "0x5C"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1C",
"green" : "0xAD",
"red" : "0xF9"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2C",
"green" : "0xB5",
"red" : "0xFC"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE1",
"green" : "0xF4",
"red" : "0xFE"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x0A",
"green" : "0x25",
"red" : "0x33"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x06",
"green" : "0x85",
"red" : "0xC6"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x03",
"green" : "0x93",
"red" : "0xDD"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x04",
"green" : "0x6A",
"red" : "0x9F"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xCC",
"green" : "0xF5",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "Damus dark-gray.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "Damus dark.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -1,23 +0,0 @@
{
"images" : [
{
"filename" : "special-features.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "special-features.svg",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "special-features.svg",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "stars-bg.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

View File

@@ -1,328 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="500"
height="130"
viewBox="0 0 132.29166 34.395832"
version="1.1"
id="svg8"
inkscape:version="0.92.1 r15371"
sodipodi:docname="ActivityPub-logo.svg">
<title
id="title4590">ActivityPub logo</title>
<defs
id="defs2">
<linearGradient
id="AP-4-0"
osb:paint="solid">
<stop
style="stop-color:#5e5e5e;stop-opacity:1;"
offset="0"
id="stop5660" />
</linearGradient>
<linearGradient
id="linearGradient5640"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5638" />
</linearGradient>
<linearGradient
id="linearGradient5634"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5632" />
</linearGradient>
<linearGradient
id="linearGradient5628"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5626" />
</linearGradient>
<linearGradient
id="AP-3-7"
osb:paint="solid">
<stop
style="stop-color:#c678c5;stop-opacity:1;"
offset="0"
id="stop5498" />
</linearGradient>
<linearGradient
id="AP-2-3"
osb:paint="solid">
<stop
style="stop-color:#6d6d6d;stop-opacity:1;"
offset="0"
id="stop5230" />
</linearGradient>
<linearGradient
id="AP1-5"
osb:paint="solid">
<stop
style="stop-color:#f1007e;stop-opacity:1;"
offset="0"
id="stop5212" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#AP-3-7"
id="linearGradient5749"
gradientUnits="userSpaceOnUse"
x1="3319.292"
y1="-1291.2802"
x2="3344.3645"
y2="-1291.2802" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP1-5"
id="linearGradient7297-7"
gradientUnits="userSpaceOnUse"
x1="3241.6836"
y1="-1355.4329"
x2="3254.9529"
y2="-1355.4329" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP-2-3"
id="linearGradient7303-7"
gradientUnits="userSpaceOnUse"
x1="3225.7603"
y1="-1355.4329"
x2="3239.0295"
y2="-1355.4329" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP1-5"
id="linearGradient8308"
gradientUnits="userSpaceOnUse"
x1="3241.6836"
y1="-1355.4329"
x2="3254.9529"
y2="-1355.4329" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP1-5"
id="linearGradient8310"
gradientUnits="userSpaceOnUse"
x1="3241.6836"
y1="-1355.4329"
x2="3254.9529"
y2="-1355.4329" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP1-5"
id="linearGradient8312"
gradientUnits="userSpaceOnUse"
x1="3241.6836"
y1="-1355.4329"
x2="3254.9529"
y2="-1355.4329" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP-2-3"
id="linearGradient8314"
gradientUnits="userSpaceOnUse"
x1="3225.7603"
y1="-1355.4329"
x2="3239.0295"
y2="-1355.4329"
gradientTransform="matrix(3.7000834,0,0,3.7000834,-11935.582,4544.6634)" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP-2-3"
id="linearGradient5188"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.42732603,0,0,0.42732603,-1363.3009,454.91899)"
x1="3269.126"
y1="-1354.6217"
x2="3322.1943"
y2="-1354.6217" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP-2-3"
id="linearGradient4523"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(3.5811973,0,0,3.5811973,-11532.084,4918.1922)"
x1="3269.126"
y1="-1354.6217"
x2="3322.1943"
y2="-1354.6217" />
<linearGradient
inkscape:collect="always"
xlink:href="#AP1-5"
id="linearGradient4526"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(3.5811973,0,0,3.5811973,-11528.758,4918.1922)"
x1="3323.9951"
y1="-1356.5363"
x2="3349.0676"
y2="-1356.5363" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="0.14509804"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="395.506"
inkscape:cy="-201.19903"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-global="true"
showguides="false"
inkscape:guide-bbox="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:showpageshadow="false"
borderlayer="false"
units="px">
<inkscape:grid
type="xygrid"
id="grid4572"
enabled="false"
originx="7.1437514"
originy="-404.28382" />
<inkscape:grid
type="axonomgrid"
id="grid4574"
units="mm"
empspacing="12"
originx="7.1437514"
originy="-404.28382"
enabled="false" />
<sodipodi:guide
position="3278.981,1256.5057"
orientation="0,1"
id="guide5059"
inkscape:locked="false" />
<sodipodi:guide
position="3278.981,1238.2495"
orientation="0,1"
id="guide5061"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>ActivityPub logo</dc:title>
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
<dc:date>2017-04-15</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Robert Martinez</dc:title>
</cc:Agent>
</dc:creator>
<dc:subject>
<rdf:Bag>
<rdf:li>ActivityPub</rdf:li>
</rdf:Bag>
</dc:subject>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
style="opacity:1"
transform="translate(7.1437516,141.67967)">
<path
style="fill:#000000;stroke-width:0.26458335"
d=""
id="path5497"
inkscape:connector-curvature="0" />
<g
id="g5197"
transform="translate(1.3229166)">
<g
id="g5132-90"
style="fill:url(#linearGradient7297-7);fill-opacity:1"
transform="matrix(0.9789804,0,0,0.9789804,-3157.9561,1202.4422)">
<g
transform="matrix(0.2553682,0,0,0.2553682,2615.9213,-1125.3113)"
id="g5080-78"
style="fill:url(#linearGradient8312);fill-opacity:1">
<path
inkscape:connector-curvature="0"
id="path5404-0-0"
d="m 2450.431,-937.13662 51.9615,30 v 12 l -51.9615,30 v -12 l 41.5693,-24 -41.5692,-24 z"
style="fill:url(#linearGradient8308);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="cccccccc" />
<path
sodipodi:nodetypes="cccc"
style="fill:url(#linearGradient8310);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2450.431,-913.13662 20.7847,12 -20.7847,12 z"
id="path5406-6-3"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g5127-1"
style="fill:url(#linearGradient7303-7);fill-opacity:1"
transform="matrix(0.9789804,0,0,0.9789804,-3157.9561,1202.4422)">
<path
id="path5467-2-0"
transform="matrix(0.27026418,0,0,0.27026418,3225.7603,-1228.2597)"
d="M 49.097656,-504.56641 0,-476.2207 v 11.33789 l 39.277344,-22.67578 v 45.35351 l 9.820312,5.66992 z m -19.638672,34.01563 -19.6406246,11.33789 19.6406246,11.33789 z"
style="fill:url(#linearGradient8314);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.25000042px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g5203"
transform="matrix(2.2173353,0,0,2.2173353,-35.445741,150.88402)">
<g
id="g4523">
<path
sodipodi:nodetypes="scscscscsscscscscscccccccccccccccscsccccscscccccccccccscsccccscsccccccccccccscscccsccccscscsccccscccccccccccccccccccccccccccccscssccccccccc"
inkscape:connector-curvature="0"
id="text5037-6"
transform="matrix(0.1193249,0,0,0.1193249,12.763965,-131.94382)"
d="m 263.22656,34.349609 c -1.66644,0 -2.95278,0.477436 -3.85742,1.429688 -0.90464,0.904639 -1.35742,2.069669 -1.35742,3.498047 0,1.428378 0.45278,2.59536 1.35742,3.5 0.90464,0.857027 2.19098,1.285156 3.85742,1.285156 1.66644,0 2.99818,-0.428129 3.99805,-1.285156 0.99986,-0.857027 1.5,-2.024009 1.5,-3.5 0,-1.475991 -0.50014,-2.665673 -1.5,-3.570313 -0.99987,-0.904639 -2.33161,-1.357422 -3.99805,-1.357422 z m 43.95117,0 c -1.66644,0 -2.95082,0.477436 -3.85546,1.429688 -0.90464,0.904639 -1.35743,2.069669 -1.35743,3.498047 0,1.428378 0.45279,2.59536 1.35743,3.5 0.90464,0.857027 2.18902,1.285156 3.85546,1.285156 1.66645,0 3.00014,-0.428129 4,-1.285156 0.99987,-0.857027 1.5,-2.024009 1.5,-3.5 0,-1.475991 -0.50013,-2.665673 -1.5,-3.570313 -0.99986,-0.904639 -2.33355,-1.357422 -4,-1.357422 z m -118.46166,0.357422 -14.49805,50.351563 h 8.92774 l 2.92773,-11.285156 h 11.78516 l 3.07031,11.285156 h 9.42773 L 195.78638,34.707031 Z m 58.71166,5.285157 -8.49804,2.642578 v 6.71289 h -3.92774 v 7.570313 h 3.92774 v 18.71289 c 0,3.713784 0.66684,6.356519 2,7.927735 1.38076,1.571216 3.42866,2.355468 6.14258,2.355468 1.5236,0 2.9747,-0.189411 4.35546,-0.570312 1.38077,-0.333288 2.59511,-0.761418 3.64258,-1.285156 L 254,77.273438 c -0.66658,0.285675 -1.28607,0.49974 -1.85742,0.642578 -0.57135,0.142837 -1.2155,0.214843 -1.92969,0.214843 -1.04748,0 -1.78438,-0.430082 -2.21289,-1.287109 -0.3809,-0.857027 -0.57227,-2.308127 -0.57227,-4.355469 V 56.917969 h 6.92774 v -7.570313 h -6.92774 z m 80.23243,0 -8.49805,2.642578 v 6.71289 h -3.92969 v 7.570313 h 3.92969 v 18.71289 c 0,3.713784 0.66489,6.356519 1.99805,7.927735 1.38076,1.571216 3.42866,2.355468 6.14257,2.355468 1.52361,0 2.97666,-0.189411 4.35743,-0.570312 1.38076,-0.333288 2.5951,-0.761418 3.64257,-1.285156 l -1.07226,-6.785156 c -0.66658,0.285675 -1.28607,0.49974 -1.85742,0.642578 -0.57135,0.142837 -1.21355,0.214843 -1.92774,0.214843 -1.04747,0 -1.78437,-0.430082 -2.21289,-1.287109 -0.3809,-0.857027 -0.57226,-2.308127 -0.57226,-4.355469 V 56.917969 h 6.92773 v -7.570313 h -6.92773 z m -135.65894,6.855468 h 0.28516 l 1.14453,7.857422 2.85547,11.640625 h -8.2832 l 2.78515,-11.570312 z m 31.94605,1.572266 c -4.33275,0 -7.61963,1.570458 -9.85743,4.71289 -2.23779,3.142433 -3.35546,7.833061 -3.35546,14.070313 0,2.856756 0.21406,5.452139 0.64257,7.785156 0.47613,2.285405 1.23768,4.261293 2.28516,5.927735 1.04748,1.618828 2.38117,2.880516 4,3.785156 1.66644,0.857027 3.71238,1.285156 6.14062,1.285156 1.66645,0 3.33356,-0.238718 5,-0.714844 1.66645,-0.476126 3.09485,-1.190326 4.28516,-2.142578 l -1.78516,-6.570312 c -0.71418,0.476126 -1.50039,0.881555 -2.35742,1.214844 -0.80941,0.285675 -1.80968,0.427734 -3,0.427734 -2.23779,0 -3.88025,-1.022971 -4.92773,-3.070313 -0.99987,-2.047342 -1.5,-4.690077 -1.5,-7.927734 0,-3.856621 0.50013,-6.641415 1.5,-8.355469 0.99986,-1.761666 2.50027,-2.642578 4.5,-2.642578 1.09509,0 2.02335,0.117406 2.78515,0.355469 0.80942,0.19045 1.64298,0.501174 2.5,0.929687 l 2,-7.070312 c -1.04747,-0.571351 -2.26181,-1.048787 -3.64257,-1.429688 -1.33316,-0.3809 -3.07033,-0.570312 -5.21289,-0.570312 z m 35.06445,0.927734 v 35.710938 h 8.5 V 49.347656 Z m 11.05469,0 12.64257,36.066406 h 5.7129 l 11.99804,-36.066406 h -9.14062 l -4.42774,18.570313 -0.78711,5.71289 h -0.28515 l -0.85742,-5.642578 -4.92774,-18.640625 z m 32.89843,0 v 35.710938 h 8.49805 V 49.347656 Z m 33.53125,0 12.42774,35.710938 c -0.28568,1.571216 -0.64375,2.832904 -1.07227,3.785156 -0.42851,0.952252 -0.92865,1.641799 -1.5,2.070312 -0.52374,0.476127 -1.11858,0.714844 -1.78515,0.714844 -0.61897,0.04761 -1.23846,-0.04905 -1.85743,-0.287109 l -1.42773,7.285156 c 0.66658,0.380901 1.45278,0.642319 2.35742,0.785156 0.95225,0.190451 1.92787,0.28711 2.92774,0.28711 1.42837,0 2.64271,-0.430083 3.64257,-1.28711 1.04748,-0.809414 1.97575,-1.999096 2.78516,-3.570312 0.80941,-1.571216 1.57097,-3.475098 2.28516,-5.712891 0.71419,-2.237792 1.47574,-4.761168 2.28515,-7.570312 l 8.92774,-32.210938 h -8.71289 l -4.14258,19.998047 -0.64258,5.642578 h -0.35742 l -0.92774,-5.572265 -5,-20.06836 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:8.52205467px;line-height:2.53632545px;font-family:'PT Sans Narrow';-inkscape-font-specification:'PT Sans Narrow Bold Condensed';letter-spacing:0.22319667px;word-spacing:2.60024095px;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:url(#linearGradient4523);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.27365798px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
id="text5065-3"
transform="matrix(0.1193249,0,0,0.1193249,12.763965,-131.94382)"
d="m 386.9082,34.349609 c -2.04734,0 -4.09523,0.119359 -6.14258,0.357422 -2.04734,0.190451 -3.92657,0.476521 -5.64062,0.857422 v 49.494141 h 8.99805 V 67.845703 c 0.19045,0.04761 0.49922,0.09497 0.92773,0.142578 l 1.42969,0.142578 h 1.35742 0.92773 c 2.04735,0 4.02324,-0.30877 5.92774,-0.927734 1.95212,-0.618964 3.66659,-1.619234 5.14258,-3 1.47599,-1.380766 2.64102,-3.165289 3.49804,-5.355469 0.90464,-2.19018 1.35743,-4.857568 1.35743,-8 0,-3.47572 -0.52284,-6.285167 -1.57032,-8.427734 -1.04747,-2.19018 -2.40582,-3.879997 -4.07226,-5.070313 -1.66644,-1.190315 -3.57033,-1.976521 -5.71289,-2.357421 -2.09496,-0.428514 -4.23756,-0.642579 -6.42774,-0.642579 z m 51.72461,0.714844 v 48.564453 c 1.14271,0.571352 2.76052,1.09614 4.85547,1.572266 2.14257,0.476126 4.47653,0.71289 7,0.71289 4.61842,0 8.25948,-1.570458 10.92578,-4.71289 2.66631,-3.190045 4,-8.164791 4,-14.925781 0,-6.237252 -0.95292,-10.761169 -2.85742,-13.570313 -1.85689,-2.809144 -4.47497,-4.214844 -7.85547,-4.214844 -3.19004,0 -5.64141,1.047624 -7.35547,3.142578 h -0.21484 V 35.064453 Z m -50.86719,7.285156 c 0.99987,0 1.95279,0.142059 2.85743,0.427735 0.90464,0.285675 1.68889,0.761158 2.35547,1.427734 0.71418,0.618964 1.26167,1.477176 1.64257,2.572266 0.42852,1.09509 0.64258,2.428784 0.64258,4 0,1.856891 -0.21406,3.402697 -0.64258,4.640625 -0.42851,1.190315 -1.02335,2.143233 -1.78515,2.857422 -0.71419,0.666576 -1.54775,1.142058 -2.5,1.427734 -0.95225,0.285676 -1.95057,0.429687 -2.99805,0.429687 -0.28568,10e-7 -0.83316,-0.02465 -1.64258,-0.07227 -0.7618,-0.09522 -1.28659,-0.189931 -1.57226,-0.285156 v -17.06836 c 0.95225,-0.238063 2.16658,-0.357422 3.64257,-0.357422 z m 20.31836,6.998047 v 23.210938 c 0,2.666306 0.21407,4.880911 0.64258,6.642578 0.42852,1.714054 1.04606,3.070448 1.85547,4.070312 0.80942,0.999865 1.78699,1.691365 2.92969,2.072266 1.1427,0.428513 2.45174,0.642578 3.92773,0.642578 2.28541,0 4.18929,-0.547488 5.71289,-1.642578 1.57122,-1.09509 2.81021,-2.476137 3.71485,-4.142578 h 0.21289 l 1.5,4.857422 h 6.42773 c -0.3809,-1.523604 -0.64232,-3.215374 -0.78515,-5.072266 -0.14284,-1.904504 -0.21485,-3.833039 -0.21485,-5.785156 V 49.347656 h -8.49804 v 23.853516 c -0.38091,1.380765 -1.02505,2.572401 -1.92969,3.572266 -0.90464,0.952252 -2.02232,1.427734 -3.35547,1.427734 -1.38077,0 -2.33368,-0.547488 -2.85742,-1.642578 -0.52374,-1.09509 -0.78516,-3.046325 -0.78516,-5.855469 V 49.347656 Z m 43.83204,6.927735 c 1.61882,0 2.80851,0.858211 3.57031,2.572265 0.7618,1.666441 1.14258,4.307223 1.14258,7.925782 0,4.094684 -0.47549,7.023489 -1.42774,8.785156 -0.95225,1.714054 -2.3333,2.572265 -4.14258,2.572265 -1.61882,0 -2.92787,-0.26337 -3.92773,-0.787109 V 59.990234 c 0.80941,-2.475855 2.40453,-3.714843 4.78516,-3.714843 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:8.52205467px;line-height:2.53632545px;font-family:'PT Sans Narrow';-inkscape-font-specification:'PT Sans Narrow Bold Condensed';letter-spacing:0.22319667px;word-spacing:2.60024095px;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:url(#linearGradient4526);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.24196777px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,12 +0,0 @@
{
"images" : [
{
"filename" : "ActivityPub-logo.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,12 +0,0 @@
{
"images" : [
{
"filename" : "atproto.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

View File

@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "shadow-2.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "shadow.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

View File

@@ -1,20 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,12 +0,0 @@
{
"images" : [
{
"filename" : "mutiny.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,12 +0,0 @@
{
"images" : [
{
"filename" : "rss.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

@@ -11,12 +11,10 @@ import SwiftUI
class DamusColors {
static let adaptableGrey = Color("DamusAdaptableGrey")
static let adaptableBlack = Color("DamusAdaptableBlack")
static let adaptableWhite = Color("DamusAdaptableWhite")
static let white = Color("DamusWhite")
static let black = Color("DamusBlack")
static let brown = Color("DamusBrown")
static let yellow = Color("DamusYellow")
static let gold = hex_col(r: 226, g: 168, b: 0)
static let lightGrey = Color("DamusLightGrey")
static let mediumGrey = Color("DamusMediumGrey")
static let darkGrey = Color("DamusDarkGrey")
@@ -24,34 +22,5 @@ class DamusColors {
static let purple = Color("DamusPurple")
static let deepPurple = Color("DamusDeepPurple")
static let blue = Color("DamusBlue")
static let bitcoin = Color("Bitcoin")
static let success = Color("DamusSuccessPrimary")
static let successSecondary = Color("DamusSuccessSecondary")
static let successTertiary = Color("DamusSuccessTertiary")
static let successQuaternary = Color("DamusSuccessQuaternary")
static let successBorder = Color("DamusSuccessBorder")
static let warning = Color("DamusWarningPrimary")
static let warningSecondary = Color("DamusWarningSecondary")
static let warningTertiary = Color("DamusWarningTertiary")
static let warningQuaternary = Color("DamusWarningQuaternary")
static let warningBorder = Color("DamusWarningBorder")
static let danger = Color("DamusDangerPrimary")
static let dangerSecondary = Color("DamusDangerSecondary")
static let dangerTertiary = Color("DamusDangerTertiary")
static let dangerQuaternary = Color("DamusDangerQuaternary")
static let dangerBorder = Color("DamusDangerBorder")
static let neutral1 = Color("DamusNeutral1")
static let neutral3 = Color("DamusNeutral3")
static let neutral6 = Color("DamusNeutral6")
static let pink = Color(red: 211/255.0, green: 76/255.0, blue: 217/255.0)
static let lighterPink = Color(red: 248/255.0, green: 105/255.0, blue: 182/255.0)
static let lightBackgroundPink = Color(red: 0xF8/255.0, green: 0xE7/255.0, blue: 0xF8/255.0)
}
func hex_col(r: UInt8, g: UInt8, b: UInt8) -> Color {
return Color(.sRGB,
red: Double(r) / Double(0xff),
green: Double(g) / Double(0xff),
blue: Double(b) / Double(0xff),
opacity: 1.0)
}

View File

@@ -10,7 +10,11 @@ import SwiftUI
struct EndBlock: View {
let height: CGFloat
init(height: Float = 10) {
init () {
self.height = 10.0
}
init (height: Float) {
self.height = CGFloat(height)
}

View File

@@ -8,21 +8,15 @@
import SwiftUI
struct GradientButtonStyle: ButtonStyle {
let padding: CGFloat
init(padding: CGFloat = 16.0) {
self.padding = padding
}
func makeBody(configuration: Self.Configuration) -> some View {
return configuration.label
.padding(padding)
.padding()
.foregroundColor(Color.white)
.background {
RoundedRectangle(cornerRadius: 12)
.fill(PinkGradient)
.fill(PinkGradient.gradient)
}
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.scaleEffect(configuration.isPressed ? 0.8 : 1)
}
}

View File

@@ -1,30 +0,0 @@
//
// DamusBackground.swift
// damus
//
// Created by William Casarin on 2023-07-12.
//
import Foundation
import SwiftUI
struct DamusBackground: View {
let maxHeight: CGFloat
init(maxHeight: CGFloat = 250.0) {
self.maxHeight = maxHeight
}
var body: some View {
Image("login-header")
.resizable()
.frame(maxWidth: .infinity, maxHeight: maxHeight, alignment: .center)
.ignoresSafeArea()
}
}
struct DamusBackground_Previews: PreviewProvider {
static var previews: some View {
DamusBackground()
}
}

View File

@@ -1,30 +0,0 @@
//
// DamusLightGradient.swift
// damus
//
// Created by eric on 9/8/23.
//
import SwiftUI
fileprivate let damus_grad_c1 = hex_col(r: 0xd3, g: 0x2d, b: 0xc3)
fileprivate let damus_grad_c2 = hex_col(r: 0x33, g: 0xc5, b: 0xbc)
fileprivate let damus_grad = [damus_grad_c1, damus_grad_c2]
struct DamusLightGradient: View {
var body: some View {
DamusLightGradient.gradient
.opacity(0.5)
.edgesIgnoringSafeArea([.top,.bottom])
}
static var gradient: LinearGradient {
LinearGradient(colors: damus_grad, startPoint: .topLeading, endPoint: .bottomTrailing)
}
}
struct DamusLightGradient_Previews: PreviewProvider {
static var previews: some View {
DamusLightGradient()
}
}

View File

@@ -7,7 +7,7 @@
import SwiftUI
fileprivate let gold_grad_c1 = DamusColors.gold
fileprivate let gold_grad_c1 = hex_col(r: 226, g: 168, b: 0)
fileprivate let gold_grad_c2 = hex_col(r: 249, g: 243, b: 100)
fileprivate let gold_grad = [gold_grad_c2, gold_grad_c1]

View File

@@ -1,26 +0,0 @@
//
// GrayGradient.swift
// damus
//
// Created by klabo on 7/20/23.
//
import SwiftUI
let GrayGradient = LinearGradient(gradient:
Gradient(colors: [Color(#colorLiteral(red: 0.9764705882, green: 0.9803921569, blue: 0.9803921569, alpha: 1))]),
startPoint: .leading,
endPoint: .trailing)
struct GrayGradientView: View {
var body: some View {
GrayGradient
.edgesIgnoringSafeArea([.top, .bottom])
}
}
struct GrayGradient_Previews: PreviewProvider {
static var previews: some View {
GrayGradientView()
}
}

View File

@@ -1,15 +0,0 @@
//
// MutinyGradient.swift
// damus
//
// Created by eric on 3/9/24.
//
import SwiftUI
fileprivate let mutiny_grad_c1 = hex_col(r: 39, g: 95, b: 161)
fileprivate let mutiny_grad_c2 = hex_col(r: 13, g: 33, b: 56)
fileprivate let mutiny_grad = [mutiny_grad_c2, mutiny_grad_c1]
let MutinyGradient: LinearGradient =
LinearGradient(colors: mutiny_grad, startPoint: .top, endPoint: .bottom)

View File

@@ -11,18 +11,20 @@ fileprivate let damus_grad_c1 = hex_col(r: 0xd3, g: 0x4c, b: 0xd9)
fileprivate let damus_grad_c2 = hex_col(r: 0xf8, g: 0x69, b: 0xb6)
fileprivate let pink_grad = [damus_grad_c1, damus_grad_c2]
let PinkGradient = LinearGradient(colors: pink_grad, startPoint: .topTrailing, endPoint: .bottom)
struct PinkGradientView: View {
struct PinkGradient: View {
var body: some View {
PinkGradient
PinkGradient.gradient
.edgesIgnoringSafeArea([.top,.bottom])
}
}
struct PinkGradientView_Previews: PreviewProvider {
static var previews: some View {
PinkGradientView()
static var gradient: LinearGradient {
LinearGradient(colors: pink_grad, startPoint: .topTrailing, endPoint: .bottom)
}
}
struct PinkGradient_Previews: PreviewProvider {
static var previews: some View {
PinkGradient()
}
}

View File

@@ -31,49 +31,6 @@ struct ShareSheet: UIViewControllerRepresentable {
}
}
// Custom UIPageControl
struct PageControlView: UIViewRepresentable {
@Binding var currentPage: Int
var numberOfPages: Int
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> UIPageControl {
let uiView = UIPageControl()
uiView.backgroundStyle = .minimal
uiView.currentPageIndicatorTintColor = UIColor(Color("DamusPurple"))
uiView.pageIndicatorTintColor = UIColor(Color("DamusLightGrey"))
uiView.currentPage = currentPage
uiView.numberOfPages = numberOfPages
uiView.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged), for: .valueChanged)
return uiView
}
func updateUIView(_ uiView: UIPageControl, context: Context) {
uiView.currentPage = currentPage
uiView.numberOfPages = numberOfPages
}
}
extension PageControlView {
final class Coordinator: NSObject {
var parent: PageControlView
init(_ parent: PageControlView) {
self.parent = parent
}
@objc func valueChanged(sender: UIPageControl) {
let currentPage = sender.currentPage
withAnimation {
parent.currentPage = currentPage
}
}
}
}
enum ImageShape {
case square
@@ -95,64 +52,42 @@ enum ImageShape {
}
}
class CarouselModel: ObservableObject {
var current_url: URL?
var fillHeight: CGFloat
var maxHeight: CGFloat
var firstImageHeight: CGFloat?
@Published var open_sheet: Bool
@Published var selectedIndex: Int
@Published var video_size: CGSize?
@Published var image_fill: ImageFill?
init(image_fill: ImageFill?) {
self.current_url = nil
self.fillHeight = 350
self.maxHeight = UIScreen.main.bounds.height * 1.2 // 1.2
self.firstImageHeight = nil
self.open_sheet = false
self.selectedIndex = 0
self.video_size = nil
self.image_fill = image_fill
}
}
// MARK: - Image Carousel
@MainActor
struct ImageCarousel<Content: View>: View {
struct ImageCarousel: View {
var urls: [MediaUrl]
let evid: NoteId
let evid: String
let state: DamusState
@ObservedObject var model: CarouselModel
let content: ((_ dismiss: @escaping (() -> Void)) -> Content)?
init(state: DamusState, evid: NoteId, urls: [MediaUrl]) {
self.urls = urls
self.evid = evid
self.state = state
let media_model = state.events.get_cache_data(evid).media_metadata_model
self._model = ObservedObject(initialValue: CarouselModel(image_fill: media_model.fill))
self.content = nil
}
init(state: DamusState, evid: NoteId, urls: [MediaUrl], @ViewBuilder content: @escaping (_ dismiss: @escaping (() -> Void)) -> Content) {
@State private var open_sheet: Bool = false
@State private var current_url: URL? = nil
@State private var image_fill: ImageFill? = nil
@State private var fillHeight: CGFloat = 350
@State private var maxHeight: CGFloat = UIScreen.main.bounds.height * 1.2 // 1.2
@State private var firstImageHeight: CGFloat? = nil
@State private var currentImageHeight: CGFloat?
@State private var selectedIndex = 0
@State private var video_size: CGSize? = nil
init(state: DamusState, evid: String, urls: [MediaUrl]) {
_open_sheet = State(initialValue: false)
_current_url = State(initialValue: nil)
let media_model = state.events.get_cache_data(evid).media_metadata_model
_image_fill = State(initialValue: media_model.fill)
self.urls = urls
self.evid = evid
self.state = state
let media_model = state.events.get_cache_data(evid).media_metadata_model
self._model = ObservedObject(initialValue: CarouselModel(image_fill: media_model.fill))
self.content = content
}
var filling: Bool {
model.image_fill?.filling == true
image_fill?.filling == true
}
var height: CGFloat {
model.firstImageHeight ?? model.image_fill?.height ?? model.fillHeight
firstImageHeight ?? image_fill?.height ?? fillHeight
}
func Placeholder(url: URL, geo_size: CGSize, num_urls: Int) -> some View {
@@ -170,36 +105,40 @@ struct ImageCarousel<Content: View>: View {
}
}
.onAppear {
if self.model.image_fill == nil, let size = state.video.size_for_url(url) {
let fill = ImageFill.calculate_image_fill(geo_size: geo_size, img_size: size, maxHeight: model.maxHeight, fillHeight: model.fillHeight)
self.model.image_fill = fill
if self.image_fill == nil, let size = state.events.lookup_media_size(url: url) {
let fill = ImageFill.calculate_image_fill(geo_size: geo_size, img_size: size, maxHeight: maxHeight, fillHeight: fillHeight)
self.image_fill = fill
}
}
}
func video_model(_ url: URL) -> VideoPlayerModel {
return state.events.get_video_player_model(url: url)
}
func Media(geo: GeometryProxy, url: MediaUrl, index: Int) -> some View {
Group {
switch url {
case .image(let url):
Img(geo: geo, url: url, index: index)
.onTapGesture {
model.open_sheet = true
open_sheet = true
}
case .video(let url):
DamusVideoPlayer(url: url, video_size: $model.video_size, controller: state.video, style: .preview(on_tap: { model.open_sheet = true }))
.onChange(of: model.video_size) { size in
DamusVideoPlayer(url: url, model: video_model(url), video_size: $video_size)
.onChange(of: video_size) { size in
guard let size else { return }
let fill = ImageFill.calculate_image_fill(geo_size: geo.size, img_size: size, maxHeight: model.maxHeight, fillHeight: model.fillHeight)
let fill = ImageFill.calculate_image_fill(geo_size: geo.size, img_size: size, maxHeight: maxHeight, fillHeight: fillHeight)
print("video_size changed \(size)")
if self.model.image_fill == nil {
if self.image_fill == nil {
print("video_size firstImageHeight \(fill.height)")
self.model.firstImageHeight = fill.height
firstImageHeight = fill.height
state.events.get_cache_data(evid).media_metadata_model.fill = fill
}
self.model.image_fill = fill
self.image_fill = fill
}
}
}
@@ -215,7 +154,7 @@ struct ImageCarousel<Content: View>: View {
.configure { view in
view.framePreloadCount = 3
}
.imageFill(for: geo.size, max: model.maxHeight, fill: model.fillHeight) { fill in
.imageFill(for: geo.size, max: maxHeight, fill: fillHeight) { fill in
state.events.get_cache_data(evid).media_metadata_model.fill = fill
// blur hash can be discarded when we have the url
// NOTE: this is the wrong place for this... we need to remove
@@ -224,9 +163,9 @@ struct ImageCarousel<Content: View>: View {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
state.events.lookup_img_metadata(url: url)?.state = .not_needed
}
self.model.image_fill = fill
image_fill = fill
if index == 0 {
self.model.firstImageHeight = fill.height
firstImageHeight = fill.height
//maxHeight = firstImageHeight ?? maxHeight
} else {
//maxHeight = firstImageHeight ?? fill.height
@@ -246,7 +185,7 @@ struct ImageCarousel<Content: View>: View {
}
var Medias: some View {
TabView(selection: $model.selectedIndex) {
TabView(selection: $selectedIndex) {
ForEach(urls.indices, id: \.self) { index in
GeometryReader { geo in
Media(geo: geo, url: urls[index], index: index)
@@ -254,22 +193,14 @@ struct ImageCarousel<Content: View>: View {
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.fullScreenCover(isPresented: $model.open_sheet) {
if let content {
FullScreenCarouselView<Content>(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex) {
content({ // Dismiss closure
model.open_sheet = false
})
}
}
else {
FullScreenCarouselView<AnyView>(video_controller: state.video, urls: urls, settings: state.settings, selectedIndex: $model.selectedIndex)
}
.fullScreenCover(isPresented: $open_sheet) {
ImageView(cache: state.events, urls: urls, disable_animation: state.settings.disable_animation)
}
.frame(height: height)
.onChange(of: model.selectedIndex) { value in
model.selectedIndex = value
.onChange(of: selectedIndex) { value in
selectedIndex = value
}
.tabViewStyle(PageTabViewStyle())
}
var body: some View {
@@ -277,11 +208,31 @@ struct ImageCarousel<Content: View>: View {
Medias
.onTapGesture { }
if urls.count > 1 {
PageControlView(currentPage: $model.selectedIndex, numberOfPages: urls.count)
.frame(maxWidth: 0, maxHeight: 0)
.padding(.top, 5)
// This is our custom carousel image indicator
CarouselDotsView(urls: urls, selectedIndex: $selectedIndex)
}
}
}
// MARK: - Custom Carousel
struct CarouselDotsView<T>: View {
let urls: [T]
@Binding var selectedIndex: Int
var body: some View {
if urls.count > 1 {
HStack {
ForEach(urls.indices, id: \.self) { index in
Circle()
.fill(index == selectedIndex ? Color("DamusPurple") : Color("DamusLightGrey"))
.frame(width: 10, height: 10)
.onTapGesture {
selectedIndex = index
}
}
}
.padding(.top, CGFloat(8))
.id(UUID())
}
}
}
@@ -338,9 +289,7 @@ public struct ImageFill {
struct ImageCarousel_Previews: PreviewProvider {
static var previews: some View {
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
let test_video_url: MediaUrl = .video(URL(string: "http://cdn.jb55.com/s/zaps-build.mp4")!)
ImageCarousel<AnyView>(state: test_damus_state, evid: test_note.id, urls: [test_video_url, url])
.environmentObject(OrientationTracker())
ImageCarousel(state: test_damus_state(), evid: "evid", urls: [url, url])
}
}

View File

@@ -9,7 +9,7 @@ import SwiftUI
struct InvoiceView: View {
@Environment(\.colorScheme) var colorScheme
let our_pubkey: Pubkey
let our_pubkey: String
let invoice: Invoice
@State var showing_select_wallet: Bool = false
@State var copied = false
@@ -39,12 +39,7 @@ struct InvoiceView: View {
if settings.show_wallet_selector {
present_sheet(.select_wallet(invoice: invoice.string))
} else {
do {
try open_with_wallet(wallet: settings.default_wallet.model, invoice: invoice.string)
}
catch {
present_sheet(.select_wallet(invoice: invoice.string))
}
open_with_wallet(wallet: settings.default_wallet.model, invoice: invoice.string)
}
} label: {
RoundedRectangle(cornerRadius: 20, style: .circular)
@@ -87,26 +82,21 @@ struct InvoiceView: View {
}
}
enum OpenWalletError: Error {
case no_wallet_to_open
case store_link_invalid
case system_cannot_open_store_link
}
func open_with_wallet(wallet: Wallet.Model, invoice: String) throws {
func open_with_wallet(wallet: Wallet.Model, invoice: String) {
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
} else {
guard let store_link = wallet.appStoreLink else {
throw OpenWalletError.no_wallet_to_open
// TODO: do something here if we don't have an appstore link
return
}
guard let url = URL(string: store_link) else {
throw OpenWalletError.store_link_invalid
return
}
guard UIApplication.shared.canOpenURL(url) else {
throw OpenWalletError.system_cannot_open_store_link
return
}
UIApplication.shared.open(url)
@@ -118,12 +108,12 @@ let test_invoice = Invoice(description: .description("this is a description"), a
struct InvoiceView_Previews: PreviewProvider {
static var previews: some View {
InvoiceView(our_pubkey: .empty, invoice: test_invoice, settings: test_damus_state.settings)
InvoiceView(our_pubkey: "", invoice: test_invoice, settings: test_damus_state().settings)
.frame(width: 300, height: 200)
}
}
func present_sheet(_ sheet: Sheets) {
notify(.present_sheet(sheet))
notify(.present_sheet, sheet)
}

View File

@@ -8,7 +8,7 @@
import SwiftUI
struct InvoicesView: View {
let our_pubkey: Pubkey
let our_pubkey: String
var invoices: [Invoice]
let settings: UserSettingsStore
@@ -29,7 +29,7 @@ struct InvoicesView: View {
struct InvoicesView_Previews: PreviewProvider {
static var previews: some View {
InvoicesView(our_pubkey: test_note.pubkey, invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)], settings: test_damus_state.settings)
InvoicesView(our_pubkey: "", invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)], settings: test_damus_state().settings)
.frame(width: 300)
}
}

View File

@@ -9,19 +9,19 @@ import SwiftUI
struct NIP05Badge: View {
let nip05: NIP05
let pubkey: Pubkey
let pubkey: String
let contacts: Contacts
let show_domain: Bool
let profiles: Profiles
let clickable: Bool
@Environment(\.openURL) var openURL
init(nip05: NIP05, pubkey: Pubkey, contacts: Contacts, show_domain: Bool, profiles: Profiles) {
init (nip05: NIP05, pubkey: String, contacts: Contacts, show_domain: Bool, clickable: Bool) {
self.nip05 = nip05
self.pubkey = pubkey
self.contacts = contacts
self.show_domain = show_domain
self.profiles = profiles
self.clickable = clickable
}
var nip05_color: Bool {
@@ -32,47 +32,34 @@ struct NIP05Badge: View {
Group {
if nip05_color {
LINEAR_GRADIENT
.mask(Image("verified.fill")
.mask(Image("check-circle.fill")
.resizable()
).frame(width: 18, height: 18)
).frame(width: 14, height: 14)
} else if show_domain {
Image("verified")
.resizable()
.frame(width: 18, height: 18)
Image("check-circle.fill")
.font(.footnote)
.nip05_colorized(gradient: nip05_color)
}
}
}
var username_matches_nip05: Bool {
guard let name = profiles.lookup(id: pubkey)?.map({ p in p?.name }).value
else {
return false
}
return name.lowercased() == nip05.username.lowercased()
}
var nip05_string: String {
if nip05.username == "_" || username_matches_nip05 {
return nip05.host
} else {
return "\(nip05.username)@\(nip05.host)"
}
}
var body: some View {
HStack(spacing: 2) {
Seal
if show_domain {
Text(nip05_string)
.nip05_colorized(gradient: nip05_color)
.onTapGesture {
if let nip5url = nip05.siteUrl {
openURL(nip5url)
if clickable {
Text(nip05.host)
.nip05_colorized(gradient: nip05_color)
.onTapGesture {
if let nip5url = nip05.siteUrl {
openURL(nip5url)
}
}
}
} else {
Text(nip05.host)
.foregroundColor(.gray)
}
}
}
@@ -90,22 +77,14 @@ extension View {
}
}
func use_nip05_color(pubkey: Pubkey, contacts: Contacts) -> Bool {
func use_nip05_color(pubkey: String, contacts: Contacts) -> Bool {
return contacts.is_friend_or_self(pubkey) ? true : false
}
struct NIP05Badge_Previews: PreviewProvider {
static var previews: some View {
let test_state = test_damus_state
VStack {
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
NIP05Badge(nip05: NIP05(username: "_", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: Contacts(our_pubkey: test_pubkey), show_domain: true, profiles: test_state.profiles)
}
let test_state = test_damus_state()
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, clickable: false)
}
}

View File

@@ -1,85 +0,0 @@
//
// NeutralButtonStyle.swift
// damus
//
// Created by eric on 9/1/23.
//
import SwiftUI
enum NeutralButtonShape {
case rounded, capsule, circle
var style: NeutralButtonStyle {
switch self {
case .rounded:
return NeutralButtonStyle(padding: EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10), cornerRadius: 12)
case .capsule:
return NeutralButtonStyle(padding: EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15), cornerRadius: 20)
case .circle:
return NeutralButtonStyle(padding: EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20), cornerRadius: 9999)
}
}
}
struct NeutralButtonStyle: ButtonStyle {
let padding: EdgeInsets
let cornerRadius: CGFloat
let scaleEffect: CGFloat
init(padding: EdgeInsets = EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0), cornerRadius: CGFloat = 15, scaleEffect: CGFloat = 0.95) {
self.padding = padding
self.cornerRadius = cornerRadius
self.scaleEffect = scaleEffect
}
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(padding)
.background(DamusColors.neutral1)
.cornerRadius(cornerRadius)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(DamusColors.neutral3, lineWidth: 1)
)
.scaleEffect(configuration.isPressed ? scaleEffect : 1)
}
}
struct NeutralButtonStyle_Previews: PreviewProvider {
static var previews: some View {
VStack {
Button(action: {
print("dynamic size")
}) {
Text(verbatim: "Dynamic Size")
.padding()
}
.buttonStyle(NeutralButtonStyle())
Button(action: {
print("infinite width")
}) {
HStack {
Text(verbatim: "Infinite Width")
.padding()
}
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
}
.buttonStyle(NeutralButtonStyle())
.padding()
Button(String(stringLiteral: "Rounded Button"), action: {})
.buttonStyle(NeutralButtonShape.rounded.style)
.padding()
Button(String(stringLiteral: "Capsule Button"), action: {})
.buttonStyle(NeutralButtonShape.capsule.style)
.padding()
Button(action: {}, label: {Image("messages")})
.buttonStyle(NeutralButtonShape.circle.style)
}
}
}

View File

@@ -9,13 +9,14 @@ import SwiftUI
struct Reposted: View {
let damus: DamusState
let pubkey: Pubkey
let pubkey: String
let profile: Profile?
var body: some View {
HStack(alignment: .center) {
Image("repost")
.foregroundColor(Color.gray)
ProfileName(pubkey: pubkey, damus: damus, show_nip5_domain: false)
ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_nip5_domain: false)
.foregroundColor(Color.gray)
Text("Reposted", comment: "Text indicating that the note was reposted (i.e. re-shared).")
.foregroundColor(Color.gray)
@@ -25,7 +26,7 @@ struct Reposted: View {
struct Reposted_Previews: PreviewProvider {
static var previews: some View {
let test_state = test_damus_state
Reposted(damus: test_state, pubkey: test_state.pubkey)
let test_state = test_damus_state()
Reposted(damus: test_state, pubkey: test_state.pubkey, profile: make_test_profile())
}
}

Some files were not shown because too many files have changed in this diff Show More