Share button

This commit is contained in:
Arkadiusz Fal 2021-10-27 00:59:59 +02:00
parent b50d915d8e
commit 544dc70c5d
11 changed files with 205 additions and 19 deletions

View File

@ -74,6 +74,15 @@ struct Instance: Defaults.Serializable, Hashable, Identifiable {
Account(instanceID: id, name: "Anonymous", url: url, anonymous: true) Account(instanceID: id, name: "Anonymous", url: url, anonymous: true)
} }
var urlComponents: URLComponents {
URLComponents(string: url)!
}
var frontendHost: String {
// TODO: piped frontend link
urlComponents.host!.replacingOccurrences(of: "api", with: "")
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
hasher.combine(url) hasher.combine(url)
} }

View File

@ -2,6 +2,7 @@ import Foundation
import Siesta import Siesta
protocol VideosAPI { protocol VideosAPI {
var account: Account! { get }
var signedIn: Bool { get } var signedIn: Bool { get }
func channel(_ id: String) -> Resource func channel(_ id: String) -> Resource
@ -25,6 +26,7 @@ protocol VideosAPI {
func channelPlaylist(_ id: String) -> Resource? func channelPlaylist(_ id: String) -> Resource?
func loadDetails(_ item: PlayerQueueItem, completionHandler: @escaping (PlayerQueueItem) -> Void) func loadDetails(_ item: PlayerQueueItem, completionHandler: @escaping (PlayerQueueItem) -> Void)
func shareURL(_ item: ContentItem) -> URL
} }
extension VideosAPI { extension VideosAPI {
@ -45,4 +47,21 @@ extension VideosAPI {
completionHandler(newItem) completionHandler(newItem)
} }
} }
func shareURL(_ item: ContentItem) -> URL {
var urlComponents = account.instance.urlComponents
urlComponents.host = account.instance.frontendHost
switch item.contentType {
case .video:
urlComponents.path = "/watch"
urlComponents.query = "v=\(item.video.videoID)"
case .channel:
urlComponents.path = "/channel/\(item.channel.id)"
case .playlist:
urlComponents.path = "/playlist"
urlComponents.query = "list=\(item.playlist.id)"
}
return urlComponents.url!
}
} }

View File

@ -84,7 +84,7 @@ extension PlayerModel {
} }
@discardableResult func remove(_ item: PlayerQueueItem) -> PlayerQueueItem? { @discardableResult func remove(_ item: PlayerQueueItem) -> PlayerQueueItem? {
if let index = queue.firstIndex(where: { $0 == item }) { if let index = queue.firstIndex(where: { $0.videoID == item.videoID }) {
return queue.remove(at: index) return queue.remove(at: index)
} }
@ -138,7 +138,7 @@ extension PlayerModel {
} }
func addItemToHistory(_ item: PlayerQueueItem) { func addItemToHistory(_ item: PlayerQueueItem) {
if let index = history.firstIndex(where: { $0.video.videoID == item.video?.videoID }) { if let index = history.firstIndex(where: { $0.video?.videoID == item.video?.videoID }) {
history.remove(at: index) history.remove(at: index)
} }

View File

@ -191,6 +191,9 @@
377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.swift */; }; 377FC7E5267A084E00A6BBAF /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37AAF27F26737550007FC770 /* SearchView.swift */; };
377FC7ED267A0A0800A6BBAF /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7EC267A0A0800A6BBAF /* SwiftyJSON */; }; 377FC7ED267A0A0800A6BBAF /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7EC267A0A0800A6BBAF /* SwiftyJSON */; };
377FC7F3267A0A0800A6BBAF /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7F2267A0A0800A6BBAF /* Logging */; }; 377FC7F3267A0A0800A6BBAF /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = 377FC7F2267A0A0800A6BBAF /* Logging */; };
3784B23B272894DA00B09468 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23A272894DA00B09468 /* ShareSheet.swift */; };
3784B23D2728B85300B09468 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23C2728B85300B09468 /* ShareButton.swift */; };
3784B23E2728B85300B09468 /* ShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3784B23C2728B85300B09468 /* ShareButton.swift */; };
3788AC2726F6840700F6BAA9 /* WatchNowSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */; }; 3788AC2726F6840700F6BAA9 /* WatchNowSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */; };
3788AC2826F6840700F6BAA9 /* WatchNowSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */; }; 3788AC2826F6840700F6BAA9 /* WatchNowSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */; };
3788AC2926F6840700F6BAA9 /* WatchNowSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */; }; 3788AC2926F6840700F6BAA9 /* WatchNowSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */; };
@ -540,6 +543,8 @@
37732FEF2703A26300F04329 /* AccountValidationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidationStatus.swift; sourceTree = "<group>"; }; 37732FEF2703A26300F04329 /* AccountValidationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountValidationStatus.swift; sourceTree = "<group>"; };
37732FF32703D32400F04329 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; }; 37732FF32703D32400F04329 /* Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sidebar.swift; sourceTree = "<group>"; };
377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedContentAccessors.swift; sourceTree = "<group>"; }; 377A20A82693C9A2002842B8 /* TypedContentAccessors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypedContentAccessors.swift; sourceTree = "<group>"; };
3784B23A272894DA00B09468 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
3784B23C2728B85300B09468 /* ShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareButton.swift; sourceTree = "<group>"; };
3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchNowSection.swift; sourceTree = "<group>"; }; 3788AC2626F6840700F6BAA9 /* WatchNowSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchNowSection.swift; sourceTree = "<group>"; };
3788AC2A26F6842D00F6BAA9 /* WatchNowSectionBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchNowSectionBody.swift; sourceTree = "<group>"; }; 3788AC2A26F6842D00F6BAA9 /* WatchNowSectionBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchNowSectionBody.swift; sourceTree = "<group>"; };
378E50FA26FE8B9F00F49626 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = "<group>"; }; 378E50FA26FE8B9F00F49626 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = "<group>"; };
@ -805,6 +810,7 @@
37BA793A26DB8EE4002A0235 /* PlaylistVideosView.swift */, 37BA793A26DB8EE4002A0235 /* PlaylistVideosView.swift */,
37AAF27D26737323007FC770 /* PopularView.swift */, 37AAF27D26737323007FC770 /* PopularView.swift */,
37AAF27F26737550007FC770 /* SearchView.swift */, 37AAF27F26737550007FC770 /* SearchView.swift */,
3784B23C2728B85300B09468 /* ShareButton.swift */,
376B2E0626F920D600B1D64D /* SignInRequiredView.swift */, 376B2E0626F920D600B1D64D /* SignInRequiredView.swift */,
37AAF29F26741C97007FC770 /* SubscriptionsView.swift */, 37AAF29F26741C97007FC770 /* SubscriptionsView.swift */,
37B17D9F268A1F25006AEE9B /* VideoContextMenuView.swift */, 37B17D9F268A1F25006AEE9B /* VideoContextMenuView.swift */,
@ -917,6 +923,7 @@
37992DC826CC50CD003D4C27 /* iOS */ = { 37992DC826CC50CD003D4C27 /* iOS */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3784B23A272894DA00B09468 /* ShareSheet.swift */,
37992DC726CC50BC003D4C27 /* Info.plist */, 37992DC726CC50BC003D4C27 /* Info.plist */,
); );
path = iOS; path = iOS;
@ -1173,10 +1180,10 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 37D4B0EC2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (iOS)" */; buildConfigurationList = 37D4B0EC2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (iOS)" */;
buildPhases = ( buildPhases = (
37CC3F48270CE89B00608308 /* ShellScript */,
37D4B0C52671614900C925CA /* Sources */, 37D4B0C52671614900C925CA /* Sources */,
37D4B0C62671614900C925CA /* Frameworks */, 37D4B0C62671614900C925CA /* Frameworks */,
37D4B0C72671614900C925CA /* Resources */, 37D4B0C72671614900C925CA /* Resources */,
37CC3F48270CE89B00608308 /* ShellScript */,
37A3B1932725735F000FB5EE /* Embed App Extensions */, 37A3B1932725735F000FB5EE /* Embed App Extensions */,
); );
buildRules = ( buildRules = (
@ -1205,10 +1212,10 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 37D4B0EF2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (macOS)" */; buildConfigurationList = 37D4B0EF2671614900C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (macOS)" */;
buildPhases = ( buildPhases = (
37CC3F4A270CE8D000608308 /* ShellScript */,
37D4B0CB2671614900C925CA /* Sources */, 37D4B0CB2671614900C925CA /* Sources */,
37D4B0CC2671614900C925CA /* Frameworks */, 37D4B0CC2671614900C925CA /* Frameworks */,
37D4B0CD2671614900C925CA /* Resources */, 37D4B0CD2671614900C925CA /* Resources */,
37CC3F4A270CE8D000608308 /* ShellScript */,
37A3B17127255E7F000FB5EE /* Embed App Extensions */, 37A3B17127255E7F000FB5EE /* Embed App Extensions */,
); );
buildRules = ( buildRules = (
@ -1272,10 +1279,10 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 37D4B177267164B000C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (tvOS)" */; buildConfigurationList = 37D4B177267164B000C925CA /* Build configuration list for PBXNativeTarget "Pearvidious (tvOS)" */;
buildPhases = ( buildPhases = (
37CC3F49270CE8CA00608308 /* ShellScript */,
37D4B154267164AE00C925CA /* Sources */, 37D4B154267164AE00C925CA /* Sources */,
37D4B155267164AE00C925CA /* Frameworks */, 37D4B155267164AE00C925CA /* Frameworks */,
37D4B156267164AE00C925CA /* Resources */, 37D4B156267164AE00C925CA /* Resources */,
37CC3F49270CE8CA00608308 /* ShellScript */,
); );
buildRules = ( buildRules = (
); );
@ -1498,7 +1505,7 @@
}; };
37CC3F48270CE89B00608308 /* ShellScript */ = { 37CC3F48270CE89B00608308 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 12;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
@ -1511,7 +1518,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
}; };
37CC3F49270CE8CA00608308 /* ShellScript */ = { 37CC3F49270CE8CA00608308 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
@ -1528,7 +1535,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
}; };
37CC3F4A270CE8D000608308 /* ShellScript */ = { 37CC3F4A270CE8D000608308 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
@ -1545,7 +1552,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; shellScript = "if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\nfi\n\nexport PATH\n\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
}; };
37FD43EA2704A2350073EE42 /* ShellScript */ = { 37FD43EA2704A2350073EE42 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
@ -1608,6 +1615,7 @@
37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */, 37CC3F45270CE30600608308 /* PlayerQueueItem.swift in Sources */,
37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */, 37BD07C82698B71C003EBB87 /* AppTabNavigation.swift in Sources */,
37CB12792724C76D00213B45 /* VideoURLParser.swift in Sources */, 37CB12792724C76D00213B45 /* VideoURLParser.swift in Sources */,
3784B23D2728B85300B09468 /* ShareButton.swift in Sources */,
37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */, 37EAD86B267B9C5600D9E01B /* SponsorBlockAPI.swift in Sources */,
3743CA52270F284F00E4D32B /* View+Borders.swift in Sources */, 3743CA52270F284F00E4D32B /* View+Borders.swift in Sources */,
3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */, 3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */,
@ -1677,6 +1685,7 @@
3743CA4E270EFE3400E4D32B /* PlayerQueueRow.swift in Sources */, 3743CA4E270EFE3400E4D32B /* PlayerQueueRow.swift in Sources */,
37BD672426F13D65004BE0C1 /* AppSidebarPlaylists.swift in Sources */, 37BD672426F13D65004BE0C1 /* AppSidebarPlaylists.swift in Sources */,
37B17DA2268A1F8A006AEE9B /* VideoContextMenuView.swift in Sources */, 37B17DA2268A1F8A006AEE9B /* VideoContextMenuView.swift in Sources */,
3784B23B272894DA00B09468 /* ShareSheet.swift in Sources */,
379775932689365600DD52A8 /* Array+Next.swift in Sources */, 379775932689365600DD52A8 /* Array+Next.swift in Sources */,
37B81AFC26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */, 37B81AFC26D2C9C900675966 /* VideoDetailsPaddingModifier.swift in Sources */,
37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */, 37C7A1D5267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
@ -1799,6 +1808,7 @@
37732FF52703D32400F04329 /* Sidebar.swift in Sources */, 37732FF52703D32400F04329 /* Sidebar.swift in Sources */,
379775942689365600DD52A8 /* Array+Next.swift in Sources */, 379775942689365600DD52A8 /* Array+Next.swift in Sources */,
3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */, 3748186726A7627F0084E870 /* Video+Fixtures.swift in Sources */,
3784B23E2728B85300B09468 /* ShareButton.swift in Sources */,
37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */, 37BE0BDA26A214630092E2DB /* PlayerViewController.swift in Sources */,
37E64DD226D597EB00C71877 /* SubscriptionsModel.swift in Sources */, 37E64DD226D597EB00C71877 /* SubscriptionsModel.swift in Sources */,
37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */, 37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,

View File

@ -15,6 +15,7 @@ struct PearvidiousApp: App {
.handlesExternalEvents(matching: Set(["*"])) .handlesExternalEvents(matching: Set(["*"]))
.commands { .commands {
SidebarCommands() SidebarCommands()
CommandGroup(replacing: .newItem, addition: {})
} }
#endif #endif

View File

@ -12,6 +12,7 @@ struct VideoDetails: View {
@State private var subscribed = false @State private var subscribed = false
@State private var confirmationShown = false @State private var confirmationShown = false
@State private var presentingAddToPlaylist = false @State private var presentingAddToPlaylist = false
@State private var presentingShareSheet = false
@State private var currentPage = Page.details @State private var currentPage = Page.details
@ -249,6 +250,11 @@ struct VideoDetails: View {
Group { Group {
if let video = player.currentVideo { if let video = player.currentVideo {
HStack { HStack {
ShareButton(
contentItem: ContentItem(video: video),
presentingShareSheet: $presentingShareSheet
)
Spacer() Spacer()
if let views = video.viewsCount { if let views = video.viewsCount {
@ -269,14 +275,17 @@ struct VideoDetails: View {
Spacer() Spacer()
Button { if accounts.app.supportsUserPlaylists {
presentingAddToPlaylist = true Button {
} label: { presentingAddToPlaylist = true
Label("Add to Playlist", systemImage: "text.badge.plus") } label: {
.labelStyle(.iconOnly) Label("Add to Playlist", systemImage: "text.badge.plus")
.help("Add to Playlist...") .labelStyle(.iconOnly)
.help("Add to Playlist...")
}
.buttonStyle(.plain)
.foregroundColor(.blue)
} }
.buttonStyle(.plain)
} }
.frame(maxHeight: 35) .frame(maxHeight: 35)
.foregroundColor(.secondary) .foregroundColor(.secondary)
@ -287,6 +296,17 @@ struct VideoDetails: View {
AddToPlaylistView(video: video) AddToPlaylistView(video: video)
} }
} }
#if os(iOS)
.sheet(isPresented: $presentingShareSheet) {
ShareSheet(activityItems: [
accounts.api.shareURL(contentItem)
])
}
#endif
}
private var contentItem: ContentItem {
ContentItem(video: player.currentVideo!)
} }
var detailsPage: some View { var detailsPage: some View {

View File

@ -4,6 +4,8 @@ import SwiftUI
struct ChannelPlaylistView: View { struct ChannelPlaylistView: View {
var playlist: ChannelPlaylist var playlist: ChannelPlaylist
@State private var presentingShareSheet = false
@StateObject private var store = Store<ChannelPlaylist>() @StateObject private var store = Store<ChannelPlaylist>()
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@ -44,12 +46,26 @@ struct ChannelPlaylistView: View {
#endif #endif
VerticalCells(items: items) VerticalCells(items: items)
} }
#if os(iOS)
.sheet(isPresented: $presentingShareSheet) {
ShareSheet(activityItems: [
accounts.api.shareURL(contentItem)
])
}
#endif
.onAppear { .onAppear {
resource?.addObserver(store) resource?.addObserver(store)
resource?.loadIfNeeded() resource?.loadIfNeeded()
} }
#if !os(tvOS) #if !os(tvOS)
.toolbar { .toolbar {
ToolbarItem(placement: shareButtonPlacement) {
ShareButton(
contentItem: contentItem,
presentingShareSheet: $presentingShareSheet
)
}
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
if inNavigationView { if inNavigationView {
Button("Done") { Button("Done") {
@ -64,6 +80,18 @@ struct ChannelPlaylistView: View {
.background(.thickMaterial) .background(.thickMaterial)
#endif #endif
} }
private var shareButtonPlacement: ToolbarItemPlacement {
#if os(iOS)
.navigation
#else
.automatic
#endif
}
private var contentItem: ContentItem {
ContentItem(playlist: playlist)
}
} }
struct ChannelPlaylistView_Previews: PreviewProvider { struct ChannelPlaylistView_Previews: PreviewProvider {

View File

@ -4,6 +4,8 @@ import SwiftUI
struct ChannelVideosView: View { struct ChannelVideosView: View {
let channel: Channel let channel: Channel
@State private var presentingShareSheet = false
@StateObject private var store = Store<Channel>() @StateObject private var store = Store<Channel>()
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
@ -70,6 +72,13 @@ struct ChannelVideosView: View {
#endif #endif
#if !os(tvOS) #if !os(tvOS)
.toolbar { .toolbar {
ToolbarItem(placement: shareButtonPlacement) {
ShareButton(
contentItem: contentItem,
presentingShareSheet: $presentingShareSheet
)
}
ToolbarItem { ToolbarItem {
HStack { HStack {
Text("**\(store.item?.subscriptionsString ?? "loading")** subscribers") Text("**\(store.item?.subscriptionsString ?? "loading")** subscribers")
@ -91,6 +100,13 @@ struct ChannelVideosView: View {
#else #else
.background(.thickMaterial) .background(.thickMaterial)
#endif #endif
#if os(iOS)
.sheet(isPresented: $presentingShareSheet) {
ShareSheet(activityItems: [
accounts.api.shareURL(contentItem)
])
}
#endif
.modifier(UnsubscribeAlertModifier()) .modifier(UnsubscribeAlertModifier())
.onAppear { .onAppear {
if store.item.isNil { if store.item.isNil {
@ -101,14 +117,14 @@ struct ChannelVideosView: View {
.navigationTitle(navigationTitle) .navigationTitle(navigationTitle)
} }
var resource: Resource { private var resource: Resource {
let resource = accounts.api.channel(channel.id) let resource = accounts.api.channel(channel.id)
resource.addObserver(store) resource.addObserver(store)
return resource return resource
} }
var subscriptionToggleButton: some View { private var subscriptionToggleButton: some View {
Group { Group {
if accounts.app.supportsSubscriptions && accounts.signedIn { if accounts.app.supportsSubscriptions && accounts.signedIn {
if subscriptions.isSubscribing(channel.id) { if subscriptions.isSubscribing(channel.id) {
@ -126,7 +142,19 @@ struct ChannelVideosView: View {
} }
} }
var navigationTitle: String { private var shareButtonPlacement: ToolbarItemPlacement {
#if os(iOS)
.navigation
#else
.automatic
#endif
}
private var contentItem: ContentItem {
ContentItem(channel: channel)
}
private var navigationTitle: String {
store.item?.name ?? channel.name store.item?.name ?? channel.name
} }
} }

View File

@ -0,0 +1,39 @@
import SwiftUI
struct ShareButton: View {
let contentItem: ContentItem
@Binding var presentingShareSheet: Bool
@EnvironmentObject<AccountsModel> private var accounts
var body: some View {
Button {
#if os(iOS)
presentingShareSheet = true
#else
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(shareURL, forType: .string)
#endif
} label: {
#if os(iOS)
Label("Share", systemImage: "square.and.arrow.up")
#else
EmptyView()
#endif
}
.keyboardShortcut("c")
.foregroundColor(.blue)
.buttonStyle(.plain)
.labelStyle(.iconOnly)
}
private var shareURL: String {
accounts.api.shareURL(contentItem).absoluteString
}
}
struct ShareButton_Previews: PreviewProvider {
static var previews: some View {
ShareButton(contentItem: ContentItem(video: Video.fixture), presentingShareSheet: .constant(false))
}
}

28
iOS/ShareSheet.swift Normal file
View File

@ -0,0 +1,28 @@
import Foundation
import SwiftUI
struct ShareSheet: UIViewControllerRepresentable {
typealias Callback = (_ activityType: UIActivity.ActivityType?,
_ completed: Bool,
_ returnedItems: [Any]?,
_ error: Error?) -> Void
let activityItems: [Any]
let applicationActivities = [UIActivity]()
let excludedActivityTypes = [UIActivity.ActivityType]()
let callback: Callback? = nil
func makeUIViewController(context _: Context) -> UIActivityViewController {
let controller = UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities
)
controller.excludedActivityTypes = excludedActivityTypes
controller.completionWithItemsHandler = callback
return controller
}
func updateUIViewController(_: UIActivityViewController, context _: Context) {}
}

View File

@ -6,6 +6,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
true true
} }
func applicationWillFinishLaunching(_: Notification) {
NSWindow.allowsAutomaticWindowTabbing = false
}
func applicationWillTerminate(_: Notification) { func applicationWillTerminate(_: Notification) {
ScreenSaverManager.shared.enable() ScreenSaverManager.shared.enable()
} }