Merge branch 'master' into profile-markdown
30
CHANGELOG.md
@@ -1,3 +1,33 @@
|
|||||||
|
## [0.1.8-6] - 2022-12-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Lightning wallet selector (Suhail Saqan)
|
||||||
|
- Cmd-{1,2,3,4} to switch between tabs on MacOS (Jonathan Milligan)
|
||||||
|
- Shift-Cmd-N to create a post on MacOS (Jonathan Milligan)
|
||||||
|
- Link Previews! (Sam DuBois)
|
||||||
|
- Added paste and delete buttons to add relay field (Suhail Saqan)
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Blur and opaque non-friend images rather than only display the link (Sam DuBois)
|
||||||
|
- Remove URLs in content text when image is displayed (Sam DuBois)
|
||||||
|
- Show non-image URLs as clickable link views (Sam DuBois)
|
||||||
|
- Adjusted Pay button on invoices. (Sam DuBois)
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix crash with @ sign in some posts (Pablo Fernandez)
|
||||||
|
- Swapped order of Logout and Cancel alert buttons (Terry Yiu)
|
||||||
|
- Fixed padding issue on tabbar on some devices (Sam DuBois)
|
||||||
|
- Fix post button moving after selecting from search result (OlegAba)
|
||||||
|
- Don't show white background on images in dark mode (William Casarin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[0.1.8-6]: https://github.com/damus-io/damus/releases/tag/v0.1.8-6
|
||||||
## [0.1.8-5] - 2022-12-27
|
## [0.1.8-5] - 2022-12-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
3165648B295B70D500C64604 /* LinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3165648A295B70D500C64604 /* LinkView.swift */; };
|
||||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
|
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
|
||||||
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
|
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
|
||||||
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
||||||
@@ -130,6 +131,7 @@
|
|||||||
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; };
|
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; };
|
||||||
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; };
|
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; };
|
||||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
||||||
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
||||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
||||||
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
|
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -152,6 +154,7 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
3165648A295B70D500C64604 /* LinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkView.swift; sourceTree = "<group>"; };
|
||||||
3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; };
|
3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; };
|
||||||
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
|
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
|
||||||
31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; };
|
31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; };
|
||||||
@@ -309,6 +312,7 @@
|
|||||||
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileName.swift; sourceTree = "<group>"; };
|
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileName.swift; sourceTree = "<group>"; };
|
||||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = "<group>"; };
|
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = "<group>"; };
|
||||||
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = "<group>"; };
|
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = "<group>"; };
|
||||||
|
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
||||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
||||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
|
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -480,6 +484,7 @@
|
|||||||
4C216F33286F5ACD00040376 /* DMView.swift */,
|
4C216F33286F5ACD00040376 /* DMView.swift */,
|
||||||
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
||||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
||||||
|
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
|
||||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
@@ -517,6 +522,7 @@
|
|||||||
4C90BD19283AA67F008EE7EF /* Bech32.swift */,
|
4C90BD19283AA67F008EE7EF /* Bech32.swift */,
|
||||||
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
|
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
|
||||||
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
|
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
|
||||||
|
3165648A295B70D500C64604 /* LinkView.swift */,
|
||||||
);
|
);
|
||||||
path = Util;
|
path = Util;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -808,6 +814,7 @@
|
|||||||
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */,
|
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */,
|
||||||
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
||||||
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
|
||||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
||||||
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
||||||
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
|
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
|
||||||
@@ -838,6 +845,7 @@
|
|||||||
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
|
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
|
||||||
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */,
|
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */,
|
||||||
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */,
|
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */,
|
||||||
|
3165648B295B70D500C64604 /* LinkView.swift in Sources */,
|
||||||
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */,
|
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */,
|
||||||
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
|
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
|
||||||
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
||||||
@@ -1031,7 +1039,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 5;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1070,7 +1078,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 5;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
|||||||
21
damus/Assets.xcassets/bluewallet.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "bluewallet.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/bluewallet.imageset/bluewallet.png
vendored
Normal file
|
After Width: | Height: | Size: 61 KiB |
21
damus/Assets.xcassets/cashapp.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "cashapp.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/cashapp.imageset/cashapp.png
vendored
Normal file
|
After Width: | Height: | Size: 44 KiB |
21
damus/Assets.xcassets/lnlink.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "lnlink.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/lnlink.imageset/lnlink.png
vendored
Normal file
|
After Width: | Height: | Size: 547 KiB |
21
damus/Assets.xcassets/muun.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "muun.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/muun.imageset/muun.png
vendored
Normal file
|
After Width: | Height: | Size: 25 KiB |
21
damus/Assets.xcassets/phoenix.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "phoenix.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/phoenix.imageset/phoenix.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
21
damus/Assets.xcassets/strike.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "strike.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/strike.imageset/strike.png
vendored
Normal file
|
After Width: | Height: | Size: 23 KiB |
21
damus/Assets.xcassets/walletofsatoshi.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "walletofsatoshi.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/walletofsatoshi.imageset/walletofsatoshi.png
vendored
Normal file
|
After Width: | Height: | Size: 42 KiB |
21
damus/Assets.xcassets/zebedee.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "zebedee.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/zebedee.imageset/zebedee.png
vendored
Normal file
|
After Width: | Height: | Size: 78 KiB |
21
damus/Assets.xcassets/zeusln.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "zeus.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
damus/Assets.xcassets/zeusln.imageset/zeus.png
vendored
Normal file
|
After Width: | Height: | Size: 109 KiB |
@@ -122,6 +122,7 @@ struct ImageCarousel: View {
|
|||||||
TabView {
|
TabView {
|
||||||
ForEach(urls, id: \.absoluteString) { url in
|
ForEach(urls, id: \.absoluteString) { url in
|
||||||
Rectangle()
|
Rectangle()
|
||||||
|
.foregroundColor(Color.clear)
|
||||||
.overlay {
|
.overlay {
|
||||||
KFAnimatedImage(url)
|
KFAnimatedImage(url)
|
||||||
.configure { view in
|
.configure { view in
|
||||||
@@ -136,6 +137,11 @@ struct ImageCarousel: View {
|
|||||||
Text(url.absoluteString)
|
Text(url.absoluteString)
|
||||||
}
|
}
|
||||||
.id(url.absoluteString)
|
.id(url.absoluteString)
|
||||||
|
.contextMenu {
|
||||||
|
Button("Copy Image") {
|
||||||
|
UIPasteboard.general.string = url.absoluteString
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,38 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct InvoiceView: View {
|
struct InvoiceView: View {
|
||||||
|
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
|
||||||
let invoice: Invoice
|
let invoice: Invoice
|
||||||
|
@State var showingSelectWallet: Bool = false
|
||||||
|
@State var inv: String = ""
|
||||||
|
|
||||||
var PayButton: some View {
|
var PayButton: some View {
|
||||||
Button("Pay") {
|
Button {
|
||||||
guard let url = URL(string: "lightning:" + invoice.string) else {
|
inv = invoice.string
|
||||||
return
|
showingSelectWallet = true
|
||||||
}
|
} label: {
|
||||||
UIApplication.shared.open(url)
|
RoundedRectangle(cornerRadius: 20)
|
||||||
|
.foregroundColor(colorScheme == .light ? .black : .white)
|
||||||
|
.overlay {
|
||||||
|
Text("Pay")
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(colorScheme == .light ? .white : .black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//.buttonStyle(.bordered)
|
||||||
|
.onTapGesture {
|
||||||
|
// Temporary solution so that the "pay" button can be clicked (Yes we need an empty tap gesture)
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
RoundedRectangle(cornerRadius: 20)
|
RoundedRectangle(cornerRadius: 10)
|
||||||
.foregroundColor(.secondary.opacity(0.1))
|
.foregroundColor(.secondary.opacity(0.1))
|
||||||
|
|
||||||
VStack(alignment: .trailing, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
HStack {
|
HStack {
|
||||||
Label("", systemImage: "bolt.fill")
|
Label("", systemImage: "bolt.fill")
|
||||||
.foregroundColor(.orange)
|
.foregroundColor(.orange)
|
||||||
@@ -36,9 +50,13 @@ struct InvoiceView: View {
|
|||||||
Text("\(invoice.amount / 1000) sats")
|
Text("\(invoice.amount / 1000) sats")
|
||||||
.font(.title)
|
.font(.title)
|
||||||
PayButton
|
PayButton
|
||||||
.zIndex(5.0)
|
.frame(height: 50)
|
||||||
|
.zIndex(10.0)
|
||||||
}
|
}
|
||||||
.padding()
|
.padding(30)
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showingSelectWallet, onDismiss: {showingSelectWallet = false}) {
|
||||||
|
SelectWalletView(showingSelectWallet: $showingSelectWallet, invoice: $inv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ struct ContentView: View {
|
|||||||
self.active_sheet = .post
|
self.active_sheet = .post
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}.ignoresSafeArea(.keyboard, edges: .bottom)
|
||||||
}
|
}
|
||||||
.safeAreaInset(edge: .top) {
|
.safeAreaInset(edge: .top) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@@ -228,6 +228,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline)
|
TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline)
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
self.connect()
|
self.connect()
|
||||||
|
|||||||
@@ -15,6 +15,19 @@
|
|||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>muun</string>
|
||||||
|
<string>zeusln</string>
|
||||||
|
<string>zebedee</string>
|
||||||
|
<string>lightning</string>
|
||||||
|
<string>squarecash</string>
|
||||||
|
<string>phoenix</string>
|
||||||
|
<string>lnlink</string>
|
||||||
|
<string>strike</string>
|
||||||
|
<string>bluewallet</string>
|
||||||
|
<string>walletofsatoshi</string>
|
||||||
|
</array>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
|||||||
@@ -190,6 +190,6 @@ class RelayPool {
|
|||||||
|
|
||||||
func add_rw_relay(_ pool: RelayPool, _ url: String) {
|
func add_rw_relay(_ pool: RelayPool, _ url: String) {
|
||||||
let url_ = URL(string: url)!
|
let url_ = URL(string: url)!
|
||||||
try! pool.add_relay(url_, info: RelayInfo.rw)
|
try? pool.add_relay(url_, info: RelayInfo.rw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,4 +24,19 @@ public class Constants {
|
|||||||
NostrEvent(id: UUID().description, content: "Hello World! This is so cool!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
|
NostrEvent(id: UUID().description, content: "Hello World! This is so cool!", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
|
||||||
NostrEvent(id: UUID().description, content: "Bonjour Le Monde", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
|
NostrEvent(id: UUID().description, content: "Bonjour Le Monde", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// New url prefixes needed to be added to LSApplicationQueriesSchemes
|
||||||
|
static let WALLETS = """
|
||||||
|
[
|
||||||
|
{"id": 0, "name": "Strike", "link": "strike:", "appStoreLink": "https://apps.apple.com/us/app/strike-bitcoin-payments/id1488724463", "image": "strike"},
|
||||||
|
{"id": 1, "name": "Cash App", "link": "squarecash://", "appStoreLink": "https://apps.apple.com/us/app/cash-app/id711923939", "image": "cashapp"},
|
||||||
|
{"id": 2, "name": "Muun", "link": "muun:", "appStoreLink": "https://apps.apple.com/us/app/muun-wallet/id1482037683", "image": "muun"},
|
||||||
|
{"id": 3, "name": "Blue Wallet", "link": "bluewallet:lightning:", "appStoreLink": "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", "image": "bluewallet"},
|
||||||
|
{"id": 4, "name": "Wallet Of Satoshi", "link": "walletofsatoshi:lightning:", "appStoreLink": "https://apps.apple.com/us/app/wallet-of-satoshi/id1438599608", "image": "walletofsatoshi"},
|
||||||
|
{"id": 5, "name": "Zebedee", "link": "zebedee:lightning:", "appStoreLink": "https://apps.apple.com/us/app/zebedee-wallet/id1484394401", "image": "zebedee"},
|
||||||
|
{"id": 6, "name": "Zeus LN", "link": "zeusln:lightning:", "appStoreLink": "https://apps.apple.com/us/app/zeus-ln/id1456038895", "image": "zeusln"},
|
||||||
|
{"id": 7, "name": "LNLink", "link": "lnlink:lightning:", "appStoreLink": "https://testflight.apple.com/join/aNY4yuuZ", "image": "lnlink"},
|
||||||
|
{"id": 8, "name": "Phoenix", "link": "phoenix://", "appStoreLink": "https://apps.apple.com/us/app/phoenix-wallet/id1544097028", "image": "phoenix"},
|
||||||
|
]
|
||||||
|
""".data(using: .utf8)!
|
||||||
}
|
}
|
||||||
|
|||||||
39
damus/Util/LinkView.swift
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// LinkView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Sam DuBois on 12/27/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import LinkPresentation
|
||||||
|
|
||||||
|
class CustomLinkView: LPLinkView {
|
||||||
|
override var intrinsicContentSize: CGSize { CGSize(width: 0, height: super.intrinsicContentSize.height) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LinkViewRepresentable: UIViewRepresentable {
|
||||||
|
|
||||||
|
typealias UIViewType = CustomLinkView
|
||||||
|
|
||||||
|
var metadata: LPLinkMetadata?
|
||||||
|
var url: URL?
|
||||||
|
|
||||||
|
func makeUIView(context: Context) -> CustomLinkView {
|
||||||
|
|
||||||
|
if let metadata {
|
||||||
|
let linkView = CustomLinkView(metadata: metadata)
|
||||||
|
return linkView
|
||||||
|
}
|
||||||
|
|
||||||
|
if let url {
|
||||||
|
let linkView = CustomLinkView(url: url)
|
||||||
|
return linkView
|
||||||
|
}
|
||||||
|
|
||||||
|
return CustomLinkView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: CustomLinkView, context: Context) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,6 +97,12 @@ func parse_digit(_ p: Parser) -> Int? {
|
|||||||
func parse_hex_char(_ p: Parser) -> Character? {
|
func parse_hex_char(_ p: Parser) -> Character? {
|
||||||
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos)
|
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos)
|
||||||
|
|
||||||
|
// Check that we're within the bounds of p.str's length
|
||||||
|
if p.pos >= p.str.count {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if let c = p.str[ind].unicodeScalars.first {
|
if let c = p.str[ind].unicodeScalars.first {
|
||||||
// hex chars
|
// hex chars
|
||||||
let d = c.value
|
let d = c.value
|
||||||
|
|||||||
@@ -17,9 +17,31 @@ struct AddRelayView: View {
|
|||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Form {
|
Form {
|
||||||
Section("Add Relay") {
|
Section("Add Relay") {
|
||||||
TextField("wss://some.relay.com", text: $relay)
|
ZStack(alignment: .leading) {
|
||||||
.autocorrectionDisabled(true)
|
HStack{
|
||||||
.textInputAutocapitalization(.never)
|
TextField("wss://some.relay.com", text: $relay)
|
||||||
|
.padding(2)
|
||||||
|
.padding(.leading, 25)
|
||||||
|
.autocorrectionDisabled(true)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
|
||||||
|
Label("", systemImage: "xmark.circle.fill")
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
.padding(.trailing, -25.0)
|
||||||
|
.opacity((relay == "") ? 0.0 : 1.0)
|
||||||
|
.onTapGesture {
|
||||||
|
self.relay = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label("", systemImage: "doc.on.clipboard")
|
||||||
|
.padding(.leading, -10)
|
||||||
|
.onTapGesture {
|
||||||
|
if let pastedrelay = UIPasteboard.general.string {
|
||||||
|
self.relay = pastedrelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event), artifacts: .just_content(event.content), size: .normal)
|
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
|
||||||
|
|
||||||
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
||||||
let bar = make_actionbar_model(ev: event, damus: damus_state)
|
let bar = make_actionbar_model(ev: event, damus: damus_state)
|
||||||
|
|||||||
@@ -102,12 +102,12 @@ struct ConfigView: View {
|
|||||||
.navigationTitle("Settings")
|
.navigationTitle("Settings")
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
.alert("Logout", isPresented: $confirm_logout) {
|
.alert("Logout", isPresented: $confirm_logout) {
|
||||||
Button("Logout") {
|
|
||||||
notify(.logout, ())
|
|
||||||
}
|
|
||||||
Button("Cancel") {
|
Button("Cancel") {
|
||||||
confirm_logout = false
|
confirm_logout = false
|
||||||
}
|
}
|
||||||
|
Button("Logout") {
|
||||||
|
notify(.logout, ())
|
||||||
|
}
|
||||||
} message: {
|
} message: {
|
||||||
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account")
|
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ struct DMView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event), artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
|
let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
|
||||||
|
|
||||||
|
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
|
||||||
.foregroundColor(is_ours ? Color.white : Color.primary)
|
.foregroundColor(is_ours ? Color.white : Color.primary)
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))
|
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))
|
||||||
|
|||||||
@@ -247,7 +247,9 @@ struct EventView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
||||||
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_images(contacts: damus.contacts, ev: event), artifacts: .just_content(content), size: self.size)
|
let should_show_img = should_show_images(contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey)
|
||||||
|
|
||||||
|
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.allowsHitTesting(!embedded)
|
.allowsHitTesting(!embedded)
|
||||||
|
|
||||||
@@ -309,7 +311,10 @@ struct EventView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// blame the porn bots for this code
|
// blame the porn bots for this code
|
||||||
func should_show_images(contacts: Contacts, ev: NostrEvent) -> Bool {
|
func should_show_images(contacts: Contacts, ev: NostrEvent, our_pubkey: String) -> Bool {
|
||||||
|
if ev.pubkey == our_pubkey {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if contacts.is_in_friendosphere(ev.pubkey) {
|
if contacts.is_in_friendosphere(ev.pubkey) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct FollowUserView: View {
|
|||||||
static let markdown = Markdown()
|
static let markdown = Markdown()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .top) {
|
HStack {
|
||||||
let pmodel = ProfileModel(pubkey: target.pubkey, damus: damus_state)
|
let pmodel = ProfileModel(pubkey: target.pubkey, damus: damus_state)
|
||||||
let followers = FollowersModel(damus_state: damus_state, target: target.pubkey)
|
let followers = FollowersModel(damus_state: damus_state, target: target.pubkey)
|
||||||
let pv = ProfileView(damus_state: damus_state, profile: pmodel, followers: followers)
|
let pv = ProfileView(damus_state: damus_state, profile: pmodel, followers: followers)
|
||||||
@@ -27,6 +27,8 @@ struct FollowUserView: View {
|
|||||||
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false)
|
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false)
|
||||||
if let about = profile?.about {
|
if let about = profile?.about {
|
||||||
Text(FollowUserView.markdown.process(about))
|
Text(FollowUserView.markdown.process(about))
|
||||||
|
.lineLimit(3)
|
||||||
|
.font(.footnote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +55,7 @@ struct FollowersView: View {
|
|||||||
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
|
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
.navigationBarTitle("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers")
|
.navigationBarTitle("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers")
|
||||||
.onAppear {
|
.onAppear {
|
||||||
@@ -80,6 +83,7 @@ struct FollowingView: View {
|
|||||||
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
|
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
following.subscribe()
|
following.subscribe()
|
||||||
|
|||||||
@@ -76,14 +76,11 @@ struct TabBar: View {
|
|||||||
VStack {
|
VStack {
|
||||||
Divider()
|
Divider()
|
||||||
HStack {
|
HStack {
|
||||||
TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, action: action)
|
TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("1")
|
||||||
TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, action: action)
|
TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("2")
|
||||||
TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, action: action)
|
TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("3")
|
||||||
TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, action: action)
|
TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("4")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,16 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import LinkPresentation
|
||||||
|
|
||||||
struct NoteArtifacts {
|
struct NoteArtifacts {
|
||||||
let content: String
|
let content: String
|
||||||
let images: [URL]
|
let images: [URL]
|
||||||
let invoices: [Invoice]
|
let invoices: [Invoice]
|
||||||
|
let links: [URL]
|
||||||
|
|
||||||
static func just_content(_ content: String) -> NoteArtifacts {
|
static func just_content(_ content: String) -> NoteArtifacts {
|
||||||
NoteArtifacts(content: content, images: [], invoices: [])
|
NoteArtifacts(content: content, images: [], invoices: [], links: [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
|||||||
let blocks = ev.blocks(privkey)
|
let blocks = ev.blocks(privkey)
|
||||||
var invoices: [Invoice] = []
|
var invoices: [Invoice] = []
|
||||||
var img_urls: [URL] = []
|
var img_urls: [URL] = []
|
||||||
|
var link_urls: [URL] = []
|
||||||
let txt = blocks.reduce("") { str, block in
|
let txt = blocks.reduce("") { str, block in
|
||||||
switch block {
|
switch block {
|
||||||
case .mention(let m):
|
case .mention(let m):
|
||||||
@@ -33,14 +36,20 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
|||||||
invoices.append(invoice)
|
invoices.append(invoice)
|
||||||
return str
|
return str
|
||||||
case .url(let url):
|
case .url(let url):
|
||||||
|
|
||||||
|
// Handle Image URLs
|
||||||
if is_image_url(url) {
|
if is_image_url(url) {
|
||||||
|
// Append Image
|
||||||
img_urls.append(url)
|
img_urls.append(url)
|
||||||
|
} else {
|
||||||
|
link_urls.append(url)
|
||||||
}
|
}
|
||||||
return str + url.absoluteString
|
|
||||||
|
return str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices)
|
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices, links: link_urls)
|
||||||
}
|
}
|
||||||
|
|
||||||
func is_image_url(_ url: URL) -> Bool {
|
func is_image_url(_ url: URL) -> Bool {
|
||||||
@@ -57,6 +66,7 @@ struct NoteContentView: View {
|
|||||||
|
|
||||||
@State var artifacts: NoteArtifacts
|
@State var artifacts: NoteArtifacts
|
||||||
|
|
||||||
|
@State var metaData: LPLinkMetadata? = nil
|
||||||
let size: EventViewKind
|
let size: EventViewKind
|
||||||
|
|
||||||
func MainContent() -> some View {
|
func MainContent() -> some View {
|
||||||
@@ -66,16 +76,33 @@ struct NoteContentView: View {
|
|||||||
|
|
||||||
if show_images && artifacts.images.count > 0 {
|
if show_images && artifacts.images.count > 0 {
|
||||||
ImageCarousel(urls: artifacts.images)
|
ImageCarousel(urls: artifacts.images)
|
||||||
|
} else if !show_images && artifacts.images.count > 0 {
|
||||||
|
ImageCarousel(urls: artifacts.images)
|
||||||
|
.blur(radius: 10)
|
||||||
|
.overlay {
|
||||||
|
Rectangle()
|
||||||
|
.opacity(0.50)
|
||||||
|
}
|
||||||
|
.cornerRadius(10)
|
||||||
}
|
}
|
||||||
if artifacts.invoices.count > 0 {
|
if artifacts.invoices.count > 0 {
|
||||||
InvoicesView(invoices: artifacts.invoices)
|
InvoicesView(invoices: artifacts.invoices)
|
||||||
.frame(width: 200)
|
}
|
||||||
|
|
||||||
|
if show_images, self.metaData != nil {
|
||||||
|
LinkViewRepresentable(metadata: self.metaData)
|
||||||
|
} else {
|
||||||
|
ForEach(artifacts.links, id:\.self) { link in
|
||||||
|
LinkViewRepresentable(url: link)
|
||||||
|
.frame(height: 50)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
MainContent()
|
MainContent()
|
||||||
|
.animation(.easeInOut, value: metaData)
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: privkey)
|
self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: privkey)
|
||||||
}
|
}
|
||||||
@@ -95,6 +122,28 @@ struct NoteContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.task {
|
||||||
|
if show_images, artifacts.links.count == 1 {
|
||||||
|
|
||||||
|
self.metaData = await getMetaData(for: artifacts.links.first!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func getMetaData(for url: URL) async -> LPLinkMetadata? {
|
||||||
|
// iOS 15 is crashing for some reason
|
||||||
|
guard #available(iOS 16, *) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let provider = LPMetadataProvider()
|
||||||
|
|
||||||
|
do {
|
||||||
|
return try await provider.startFetchingMetadata(for: url)
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +169,7 @@ struct NoteContentView_Previews: PreviewProvider {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let state = test_damus_state()
|
let state = test_damus_state()
|
||||||
let content = "hi there https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg"
|
let content = "hi there https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg"
|
||||||
let artifacts = NoteArtifacts(content: content, images: [], invoices: [])
|
let artifacts = NoteArtifacts(content: content, images: [], invoices: [], links: [])
|
||||||
NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, show_images: true, artifacts: artifacts, size: .normal)
|
NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, show_images: true, artifacts: artifacts, size: .normal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ func PostButton(action: @escaping () -> ()) -> some View {
|
|||||||
radius: 3,
|
radius: 3,
|
||||||
x: 3,
|
x: 3,
|
||||||
y: 3)
|
y: 3)
|
||||||
|
.keyboardShortcut("n", modifiers: [.command, .shift])
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostButtonContainer(action: @escaping () -> ()) -> some View {
|
func PostButtonContainer(action: @escaping () -> ()) -> some View {
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ struct ProfileView: View {
|
|||||||
@StateObject var profile: ProfileModel
|
@StateObject var profile: ProfileModel
|
||||||
@StateObject var followers: FollowersModel
|
@StateObject var followers: FollowersModel
|
||||||
@State private var showingEditProfile = false
|
@State private var showingEditProfile = false
|
||||||
|
@State var showingSelectWallet: Bool = false
|
||||||
|
@State var inv: String = ""
|
||||||
@State var is_zoomed: Bool = false
|
@State var is_zoomed: Bool = false
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
@@ -126,9 +128,14 @@ struct ProfileView: View {
|
|||||||
|
|
||||||
//@EnvironmentObject var profile: ProfileModel
|
//@EnvironmentObject var profile: ProfileModel
|
||||||
|
|
||||||
func LNButton(_ url: URL, profile: Profile) -> some View {
|
func LNButton(lud06: String?, lud16: String?, profile: Profile) -> some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
UIApplication.shared.open(url)
|
if let l = lud06 {
|
||||||
|
inv = l
|
||||||
|
} else {
|
||||||
|
inv = lud16 ?? ""
|
||||||
|
}
|
||||||
|
showingSelectWallet = true
|
||||||
}) {
|
}) {
|
||||||
Image(systemName: "bolt.circle")
|
Image(systemName: "bolt.circle")
|
||||||
.symbolRenderingMode(.palette)
|
.symbolRenderingMode(.palette)
|
||||||
@@ -138,9 +145,11 @@ struct ProfileView: View {
|
|||||||
Button {
|
Button {
|
||||||
UIPasteboard.general.string = profile.lnurl ?? ""
|
UIPasteboard.general.string = profile.lnurl ?? ""
|
||||||
} label: {
|
} label: {
|
||||||
Label("Copy LNUrl", systemImage: "doc.on.doc")
|
Label("Copy LNURL", systemImage: "doc.on.doc")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}.sheet(isPresented: $showingSelectWallet, onDismiss: {showingSelectWallet = false}) {
|
||||||
|
SelectWalletView(showingSelectWallet: $showingSelectWallet, invoice: $inv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,8 +183,8 @@ struct ProfileView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if let profile = data {
|
if let profile = data {
|
||||||
if let lnuri = profile.lightning_uri {
|
if (profile.lud06 != nil || profile.lud16 != nil) {
|
||||||
LNButton(lnuri, profile: profile)
|
LNButton(lud06: profile.lud06, lud16: profile.lud16, profile: profile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
92
damus/Views/SelectWalletView.swift
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// SelectWalletView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Suhail Saqan on 12/22/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct WalletItem : Decodable, Identifiable, Hashable {
|
||||||
|
var id: Int
|
||||||
|
var name : String
|
||||||
|
var link : String
|
||||||
|
var appStoreLink : String
|
||||||
|
var image: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SelectWalletView: View {
|
||||||
|
@Binding var showingSelectWallet: Bool
|
||||||
|
@Binding var invoice: String
|
||||||
|
@Environment(\.openURL) private var openURL
|
||||||
|
@State var invoice_copied: Bool = false
|
||||||
|
|
||||||
|
let generator = UIImpactFeedbackGenerator(style: .light)
|
||||||
|
|
||||||
|
let walletItems = try! JSONDecoder().decode([WalletItem].self, from: Constants.WALLETS)
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationView {
|
||||||
|
Form {
|
||||||
|
Section("Copy invoice") {
|
||||||
|
HStack {
|
||||||
|
Text(invoice).font(.body)
|
||||||
|
.lineLimit(2)
|
||||||
|
.truncationMode(.tail)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Image(systemName: self.invoice_copied ? "checkmark.circle" : "doc.on.doc").foregroundColor(.blue)
|
||||||
|
}.clipShape(RoundedRectangle(cornerRadius: 5)).onTapGesture {
|
||||||
|
UIPasteboard.general.string = invoice
|
||||||
|
self.invoice_copied = true
|
||||||
|
generator.impactOccurred()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section("Select a lightning wallet"){
|
||||||
|
List{
|
||||||
|
Button() {
|
||||||
|
if let url = URL(string: "lightning:\(invoice)"), UIApplication.shared.canOpenURL(url) {
|
||||||
|
openURL(url)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text("Default Wallet").font(.body).foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
}.buttonStyle(.plain)
|
||||||
|
ForEach(walletItems, id: \.self) { wallet in
|
||||||
|
Button() {
|
||||||
|
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
|
||||||
|
print("opening wallet url \(url)")
|
||||||
|
openURL(url)
|
||||||
|
} else {
|
||||||
|
if let url = URL(string: wallet.appStoreLink), UIApplication.shared.canOpenURL(url) {
|
||||||
|
openURL(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Image(wallet.image).resizable().frame(width: 32.0, height: 32.0,alignment: .center).cornerRadius(5)
|
||||||
|
Text(wallet.name).font(.body)
|
||||||
|
}
|
||||||
|
}.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
}.padding(.vertical, 2.5)
|
||||||
|
}
|
||||||
|
}.navigationBarTitle(Text("Pay the lightning invoice"), displayMode: .inline).navigationBarItems(trailing: Button(action: {
|
||||||
|
self.showingSelectWallet = false
|
||||||
|
}) {
|
||||||
|
Text("Done").bold()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SelectWalletView_Previews: PreviewProvider {
|
||||||
|
@State static var show: Bool = true
|
||||||
|
@State static var invoice: String = ""
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
SelectWalletView(showingSelectWallet: $show, invoice: $invoice)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -245,7 +245,7 @@ struct ThreadV2View: View {
|
|||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
// TODO: find another solution to prevent layout shifting and layout blocking on large responses
|
// TODO: find another solution to prevent layout shifting and layout blocking on large responses
|
||||||
reader.scrollTo("main", anchor: .center)
|
reader.scrollTo("main", anchor: .bottom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.background(GeometryReader { geometry in
|
}.background(GeometryReader { geometry in
|
||||||
|
|||||||
@@ -53,6 +53,14 @@ class ReplyTests: XCTestCase {
|
|||||||
XCTAssertEqual(blocks[0].is_text, content)
|
XCTAssertEqual(blocks[0].is_text, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAtAtEnd() {
|
||||||
|
let content = "what @"
|
||||||
|
let blocks = parse_post_blocks(content: content)
|
||||||
|
|
||||||
|
XCTAssertEqual(blocks.count, 1)
|
||||||
|
XCTAssertEqual(blocks[0].is_text, "what @")
|
||||||
|
}
|
||||||
|
|
||||||
func testHashtagsInQuote() {
|
func testHashtagsInQuote() {
|
||||||
let content = "This is my \"#awesome post\""
|
let content = "This is my \"#awesome post\""
|
||||||
let blocks = parse_post_blocks(content: content)
|
let blocks = parse_post_blocks(content: content)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ sections = [
|
|||||||
|
|
||||||
repo = 'damus-io/damus'
|
repo = 'damus-io/damus'
|
||||||
|
|
||||||
Entry = namedtuple("Entry", ["commit", "pullreq", "content", "section"])
|
Entry = namedtuple("Entry", ["commit", "pullreq", "content", "section", "author"])
|
||||||
Link = namedtuple("Link", ["ref", "content", "url"])
|
Link = namedtuple("Link", ["ref", "content", "url"])
|
||||||
|
|
||||||
|
|
||||||
@@ -46,14 +46,20 @@ def get_log_entries(commitrange):
|
|||||||
commit = None
|
commit = None
|
||||||
logs = git("log {commitrange}".format(commitrange=commitrange))
|
logs = git("log {commitrange}".format(commitrange=commitrange))
|
||||||
entries = []
|
entries = []
|
||||||
|
author = ""
|
||||||
|
|
||||||
for l in logs.split('\n'):
|
for l in logs.split('\n'):
|
||||||
m = re.match(r'^commit ([A-Fa-f0-9]{40})$', l)
|
m = re.match(r'^commit ([A-Fa-f0-9]{40})$', l)
|
||||||
|
a = re.match(r'^Author: ([^<]+)<.*$', l)
|
||||||
if m:
|
if m:
|
||||||
commit = m.group(1)
|
commit = m.group(1)
|
||||||
|
|
||||||
|
if a:
|
||||||
|
author = "(" + a.group(1)[:-1] + ")"
|
||||||
|
|
||||||
m = re.match(
|
m = re.match(
|
||||||
r'^\s+Changelog-({}): (.*)$'.format("|".join(sections)), l, re.IGNORECASE)
|
r'^\s+Changelog-({}): (.*)$'.format("|".join(sections)), l, re.IGNORECASE)
|
||||||
|
|
||||||
if not m:
|
if not m:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -73,7 +79,7 @@ def get_log_entries(commitrange):
|
|||||||
# pullreq = None
|
# pullreq = None
|
||||||
pullreq = None
|
pullreq = None
|
||||||
|
|
||||||
e = Entry(commit, pullreq, m.group(2), m.group(1).lower())
|
e = Entry(commit, pullreq, m.group(2), m.group(1).lower(), author)
|
||||||
entries.append(e)
|
entries.append(e)
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
@@ -112,9 +118,9 @@ def commit_date(commitsha):
|
|||||||
template = Template("""<%def name="group(entries)">
|
template = Template("""<%def name="group(entries)">
|
||||||
% for e in entries:
|
% for e in entries:
|
||||||
% if e.pullreq is not None:
|
% if e.pullreq is not None:
|
||||||
- ${e.content} ([#${e.pullreq}])
|
- ${e.content} ([#${e.pullreq}])
|
||||||
% else:
|
% else:
|
||||||
- ${e.content}
|
- ${e.content} ${e.author}
|
||||||
% endif
|
% endif
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
|
|||||||