diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 3eed0e2d0..65ddf6267 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -29,7 +29,6 @@ struct AnimatingCellHeight: AnimatableModifier { @State var hasHighlightMutations = false @State var searchPresented = false @State var addLinkPresented = false - @State var settingsPresented = false @State var isListScrolled = false @State var listTitle = "" @State var isEditMode: EditMode = .inactive @@ -54,7 +53,8 @@ struct AnimatingCellHeight: AnimatableModifier { } var showFeatureCards: Bool { - viewModel.listConfig.hasFeatureCards && + isEditMode == .inactive && + viewModel.listConfig.hasFeatureCards && !viewModel.hideFeatureSection && viewModel.fetcher.items.count > 0 && viewModel.searchTerm.isEmpty && @@ -179,11 +179,6 @@ struct AnimatingCellHeight: AnimatableModifier { LibraryAddLinkView() } } - .sheet(isPresented: $settingsPresented) { - NavigationView { - ProfileView() - } - } .task { if viewModel.fetcher.items.isEmpty { loadItems(isRefresh: false) @@ -210,14 +205,6 @@ struct AnimatingCellHeight: AnimatableModifier { } .frame(maxWidth: .infinity, alignment: .bottomLeading) } - - ToolbarItem(placement: .barTrailing) { - if UIDevice.isIPad, viewModel.folder == "inbox" { - Button(action: { addLinkPresented = true }, label: { - Label("Add Link", systemImage: "plus") - }) - } - } ToolbarItem(placement: UIDevice.isIPhone ? .barLeading : .barTrailing) { if enableGrid { Button( @@ -232,29 +219,31 @@ struct AnimatingCellHeight: AnimatableModifier { Button( action: { searchPresented = true }, label: { - Image(systemName: "magnifyingglass") + Image.magnifyingGlass + .foregroundColor(Color.appGrayTextContrast) } ) } ToolbarItem(placement: .barTrailing) { - if UIDevice.isIPhone { - Menu(content: { - Button(action: { - isEditMode = isEditMode == .inactive ? .active : .inactive - }, label: { - Text(isEditMode == .inactive ? "Select Multiple" : "End Multiselect") - }) - Button(action: { addLinkPresented = true }, label: { - Label("Add Link", systemImage: "plus.circle") - }) - Button(action: { settingsPresented = true }, label: { - Label(LocalText.genericProfile, systemImage: "person.circle") - }) - - }, label: { - Image.utilityMenu - }) - .foregroundColor(.appGrayTextContrast) + Button( + action: { isEditMode = isEditMode == .active ? .inactive : .active }, + label: { + Image.selectMultiple + .foregroundColor(Color.appGrayTextContrast) + } + ) + } + ToolbarItem(placement: .barTrailing) { + if viewModel.folder == "inbox" { + Button( + action: { addLinkPresented = true }, + label: { + Image.addLink + .foregroundColor(Color.appGrayTextContrast) + } + ) + } else { + EmptyView() } } ToolbarItemGroup(placement: .bottomBar) { @@ -368,7 +357,9 @@ struct AnimatingCellHeight: AnimatableModifier { @Binding var isListScrolled: Bool @Binding var prefersListLayout: Bool @Binding var isEditMode: EditMode + @State private var showAddFeedView = false @State private var showHideFeatureAlert = false + @State private var showHideFollowingAlert = false @Binding var selection: Set @ObservedObject var viewModel: HomeFeedViewModel @@ -379,23 +370,6 @@ struct AnimatingCellHeight: AnimatableModifier { @State var topItem: Models.LibraryItem? @ObservedObject var networkMonitor = NetworkMonitor() -// init(listTitle: Binding, -// isListScrolled: Binding, -// prefersListLayout: Binding, -// isEditMode: Binding, -// selection: Binding>, -// viewModel: HomeFeedViewModel, -// showFeatureCards: Bool) -// { -// self._listTitle = listTitle -// self._isListScrolled = isListScrolled -// self._prefersListLayout = prefersListLayout -// self._isEditMode = isEditMode -// self._selection = selection -// self.viewModel = viewModel -// self.showFeatureCards = showFeatureCards -// } - var filtersHeader: some View { GeometryReader { reader in ScrollView(.horizontal, showsIndicators: false) { @@ -603,8 +577,94 @@ struct AnimatingCellHeight: AnimatableModifier { } } + var redactedItems: some View { + ForEach(Array(fakeLibraryItems(dataService: dataService).enumerated()), id: \.1.unwrappedID) { _, item in + let horizontalInset = CGFloat(UIDevice.isIPad ? 20 : 10) + LibraryItemCard(item: item, viewer: dataService.currentViewer) + .listRowSeparatorTint(Color.thBorderColor) + .listRowInsets(.init(top: 0, leading: horizontalInset, bottom: 10, trailing: horizontalInset)) + }.redacted(reason: .placeholder) + } + + var emptyState: some View { + if viewModel.folder == "following" { + return AnyView( + VStack(alignment: .center, spacing: 20) { + Text("You don't have any Feed items.") + .font(Font.system(size: 18, weight: .bold)) + + Text("Add an RSS/Atom feed") + .foregroundColor(Color.blue) + .onTapGesture { + showAddFeedView = true + } + + Text("Hide the Following tab") + .foregroundColor(Color.blue) + .onTapGesture { + showHideFollowingAlert = true + } + } + .frame(minHeight: 400) + .frame(maxWidth: .infinity) + .padding() + ) + } else { + return AnyView(Group { + Spacer() + + VStack(alignment: .center, spacing: 20) { + Text("No results found for this query") + .font(Font.system(size: 18, weight: .bold)) + } + .frame(minHeight: 400) + .frame(maxWidth: .infinity) + .padding() + + Spacer() + }) + } + } + + var listItems: some View { + ForEach(Array(viewModel.fetcher.items.enumerated()), id: \.1.unwrappedID) { _, item in + let horizontalInset = CGFloat(UIDevice.isIPad ? 20 : 10) + + FeedCardNavigationLink( + item: item, + isInMultiSelectMode: viewModel.isInMultiSelectMode, + viewModel: viewModel + ) + .background(GeometryReader { geometry in + Color.clear + .preference(key: ScrollOffsetPreferenceKey.self, value: geometry.frame(in: .named("scroll")).origin) + }) + .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in + if value.y < 100, value.y > 0 { + if item.savedAt != nil, topItem != item { + setTopItem(item) + } + } + } + .listRowSeparatorTint(Color.thBorderColor) + .listRowInsets(.init(top: 0, leading: horizontalInset, bottom: 10, trailing: horizontalInset)) + .contextMenu { + menuItems(for: item) + } + .swipeActions(edge: .leading, allowsFullSwipe: true) { + ForEach(viewModel.listConfig.leadingSwipeActions, id: \.self) { action in + swipeActionButton(action: action, item: item) + } + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + ForEach(viewModel.listConfig.trailingSwipeActions, id: \.self) { action in + swipeActionButton(action: action, item: item) + } + } + } + } + var body: some View { - let horizontalInset = CGFloat(UIDevice.isIPad ? 20 : 10) VStack(spacing: 0) { Color.systemBackground.frame(height: 1) ScrollViewReader { reader in @@ -643,38 +703,13 @@ struct AnimatingCellHeight: AnimatableModifier { } } - ForEach(Array(viewModel.fetcher.items.enumerated()), id: \.1.unwrappedID) { _, item in - FeedCardNavigationLink( - item: item, - isInMultiSelectMode: viewModel.isInMultiSelectMode, - viewModel: viewModel - ) - .background(GeometryReader { geometry in - Color.clear - .preference(key: ScrollOffsetPreferenceKey.self, value: geometry.frame(in: .named("scroll")).origin) - }) - .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in - if value.y < 100, value.y > 0 { - if item.savedAt != nil, topItem != item { - setTopItem(item) - } - } - } - .listRowSeparatorTint(Color.thBorderColor) - .listRowInsets(.init(top: 0, leading: horizontalInset, bottom: 10, trailing: horizontalInset)) - .contextMenu { - menuItems(for: item) - } - .swipeActions(edge: .leading, allowsFullSwipe: true) { - ForEach(viewModel.listConfig.leadingSwipeActions, id: \.self) { action in - swipeActionButton(action: action, item: item) - } - } - .swipeActions(edge: .trailing, allowsFullSwipe: true) { - ForEach(viewModel.listConfig.trailingSwipeActions, id: \.self) { action in - swipeActionButton(action: action, item: item) - } - } + if viewModel.showLoadingBar { + redactedItems + } else if viewModel.fetcher.items.isEmpty { + emptyState + .listRowSeparator(.hidden, edges: .all) + } else { + listItems } } }, header: { @@ -698,13 +733,26 @@ struct AnimatingCellHeight: AnimatableModifier { shouldScrollToTop = true } } + .sheet(isPresented: $showAddFeedView) { + NavigationView { + LibraryAddFeedView() + } + } .alert("The Feature Section will be removed from your library. You can add it back from the filter settings in your profile.", isPresented: $showHideFeatureAlert) { Button("OK", role: .destructive) { viewModel.hideFeatureSection = true } Button(LocalText.cancelGeneric, role: .cancel) { self.showHideFeatureAlert = false } - }.introspectNavigationController { nav in + } + .alert("The Following tab will be hidden. You can add it back from the filter settings in your profile.", + isPresented: $showHideFollowingAlert) { + Button("OK", role: .destructive) { + viewModel.hideFollowingTab = true + } + Button(LocalText.cancelGeneric, role: .cancel) { self.showHideFollowingAlert = false } + } + .introspectNavigationController { nav in nav.navigationBar.shadowImage = UIImage() nav.navigationBar.setBackgroundImage(UIImage(), for: .default) } @@ -863,13 +911,24 @@ struct AnimatingCellHeight: AnimatableModifier { ScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 325, maximum: 400), spacing: 16)], alignment: .center, spacing: 30) { - ForEach(viewModel.fetcher.items) { item in - GridCardNavigationLink( - item: item, - actionHandler: { contextMenuActionHandler(item: item, action: $0) }, - isContextMenuOpen: $isContextMenuOpen, - viewModel: viewModel - ) + if viewModel.showLoadingBar { + ForEach(fakeLibraryItems(dataService: dataService)) { item in + GridCardNavigationLink( + item: item, + actionHandler: { contextMenuActionHandler(item: item, action: $0) }, + isContextMenuOpen: $isContextMenuOpen, + viewModel: viewModel + ) + }.redacted(reason: .placeholder) + } else { + ForEach(viewModel.fetcher.items) { item in + GridCardNavigationLink( + item: item, + actionHandler: { contextMenuActionHandler(item: item, action: $0) }, + isContextMenuOpen: $isContextMenuOpen, + viewModel: viewModel + ) + } } Spacer() } @@ -949,3 +1008,21 @@ struct LinkDestination: View { } } } + +func fakeLibraryItems(dataService: DataService) -> [Models.LibraryItem] { + let temp = Models.LibraryItem(context: dataService.viewContext) + temp.id = UUID().uuidString + temp.wordsCount = 100 + temp.author = "the author" + temp.siteName = "omnivore dot app" + temp.title = "This is a temporary title for a fake item" + temp.highlights = [] + temp.imageURLString = "https://localhost/" + return Array( + repeatElement(temp, count: 20) + .map { item in + item.id = UUID().uuidString + return item + } + ) +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 03f63bd66..6943fa16e 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -44,6 +44,8 @@ import Views @AppStorage(UserDefaultKey.hideFeatureSection.rawValue) var hideFeatureSection = false @AppStorage(UserDefaultKey.lastSelectedFeaturedItemFilter.rawValue) var featureFilter = FeaturedItemFilter.continueReading.rawValue + @AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false + @Published var appliedFilter: InternalFilter? { didSet { let filterKey = UserDefaults.standard.string(forKey: "lastSelected-\(folder)-filter") ?? folder @@ -152,10 +154,9 @@ import Views func loadItems(dataService: DataService, isRefresh: Bool) async { isLoading = true - showLoadingBar = true + showLoadingBar = isRefresh await fetcher.loadItems(dataService: dataService, filterState: filterState, isRefresh: isRefresh) - updateFeatureFilter(context: dataService.viewContext, filter: FeaturedItemFilter(rawValue: featureFilter)) isLoading = false @@ -164,12 +165,10 @@ import Views func loadMoreItems(dataService: DataService, filterState: FetcherFilterState, isRefresh: Bool) async { isLoading = true - showLoadingBar = true await fetcher.loadMoreItems(dataService: dataService, filterState: filterState, isRefresh: isRefresh) isLoading = false - showLoadingBar = false } func loadFeatureItems(context: NSManagedObjectContext, predicate: NSPredicate, sort: NSSortDescriptor) async -> [Models.LibraryItem] { diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/LibraryAddFeedView.swift b/apple/OmnivoreKit/Sources/App/Views/Home/LibraryAddFeedView.swift new file mode 100644 index 000000000..420fb4cf0 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Home/LibraryAddFeedView.swift @@ -0,0 +1,156 @@ + +import Introspect +import Models +import Services +import SwiftUI +import Views + +@MainActor final class LibraryAddFeedViewModel: NSObject, ObservableObject { + @Published var isLoading = false + @Published var errorMessage: String = "" + @Published var showErrorMessage: Bool = false + + @Environment(\.dismiss) private var dismiss + + func addLink(dataService: DataService, newLinkURL: String, dismiss: DismissAction) { + isLoading = true + Task { + if URL(string: newLinkURL) == nil { + error("Invalid link") + } else { + let result = try? await dataService.saveURL(id: UUID().uuidString, url: newLinkURL) + if result == nil { + error("Error adding link") + } else { + dismiss() + } + } + isLoading = false + } + } + + func error(_ msg: String) { + errorMessage = msg + showErrorMessage = true + isLoading = false + } +} + +struct LibraryAddFeedView: View { + @StateObject var viewModel = LibraryAddFeedViewModel() + + @State var newLinkURL: String = "" + @EnvironmentObject var dataService: DataService + @Environment(\.dismiss) private var dismiss + + enum FocusField: Hashable { + case addLinkEditor + } + + @FocusState private var focusedField: FocusField? + + var body: some View { + Group { + #if os(iOS) + Form { + innerBody + .navigationTitle("Add Link") + .navigationBarTitleDisplayMode(.inline) + } + #else + innerBody + #endif + } + #if os(macOS) + .padding() + #endif + .onAppear { + focusedField = .addLinkEditor + } + .navigationTitle("Add Link") + #if os(iOS) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + dismissButton + } + ToolbarItem(placement: .navigationBarTrailing) { + viewModel.isLoading ? AnyView(ProgressView()) : AnyView(addButton) + } + } + #endif + .alert(viewModel.errorMessage, + isPresented: $viewModel.showErrorMessage) { + Button(LocalText.genericOk, role: .cancel) { viewModel.showErrorMessage = false } + } + } + + var cancelButton: some View { + Button( + action: { dismiss() }, + label: { Text(LocalText.cancelGeneric).foregroundColor(.appGrayTextContrast) } + ) + } + + var pasteboardString: String? { + #if os(iOS) + UIPasteboard.general.url?.absoluteString + #else + NSPasteboard.general.string(forType: NSPasteboard.PasteboardType.URL) + #endif + } + + var innerBody: some View { + Group { + TextField("Add Link", text: $newLinkURL) + #if os(iOS) + .keyboardType(.URL) + #endif + .autocorrectionDisabled(true) + .textFieldStyle(StandardTextFieldStyle()) + .focused($focusedField, equals: .addLinkEditor) + + Button(action: { + if let url = pasteboardString { + newLinkURL = url + } else { + viewModel.error("No URL on pasteboard") + } + }, label: { + Text("Get from pasteboard") + }) + + #if os(macOS) + Spacer() + HStack { + cancelButton + Spacer() + addButton + } + .frame(maxWidth: .infinity) + #endif + } + } + + var addButton: some View { + Button( + action: { + viewModel.addLink(dataService: dataService, newLinkURL: newLinkURL, dismiss: dismiss) + }, + label: { Text("Add").bold() } + ) + .keyboardShortcut(.defaultAction) + .onSubmit { + viewModel.addLink(dataService: dataService, newLinkURL: newLinkURL, dismiss: dismiss) + } + .disabled(viewModel.isLoading) + } + + var dismissButton: some View { + Button( + action: { dismiss() }, + label: { Text(LocalText.genericClose) } + ) + .disabled(viewModel.isLoading) + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/LibrarySearchView.swift b/apple/OmnivoreKit/Sources/App/Views/Home/LibrarySearchView.swift index cd49f94fa..523867779 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/LibrarySearchView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/LibrarySearchView.swift @@ -33,11 +33,11 @@ performTypeahead(searchTerm) } - func performSearch(_: String) { -// let term = searchTerm.trimmingCharacters(in: Foundation.CharacterSet.whitespacesAndNewlines) -// viewModel.saveRecentSearch(dataService: dataService, searchTerm: term) -// recents = viewModel.recentSearches(dataService: dataService) -// homeFeedViewModel.searchTerm = term + func performSearch(_ searchTerm: String) { + let term = searchTerm.trimmingCharacters(in: Foundation.CharacterSet.whitespacesAndNewlines) + viewModel.saveRecentSearch(dataService: dataService, searchTerm: term) + recents = viewModel.recentSearches(dataService: dataService) + homeFeedViewModel.searchTerm = term dismiss() } diff --git a/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift b/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift index fd36257a8..ecf6bd394 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LibraryTabView.swift @@ -19,6 +19,7 @@ struct LibraryTabView: View { @EnvironmentObject var dataService: DataService @EnvironmentObject var audioController: AudioController + @AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false @AppStorage(UserDefaultKey.lastSelectedTabItem.rawValue) var selectedTab = "inbox" @State var showExpandedAudioPlayer = false @@ -53,11 +54,13 @@ struct LibraryTabView: View { var body: some View { VStack(spacing: 0) { TabView(selection: $selectedTab) { - NavigationView { - HomeFeedContainerView(viewModel: followingViewModel) - .navigationBarTitleDisplayMode(.inline) - .navigationViewStyle(.stack) - }.tag("following") + if !hideFollowingTab { + NavigationView { + HomeFeedContainerView(viewModel: followingViewModel) + .navigationBarTitleDisplayMode(.inline) + .navigationViewStyle(.stack) + }.tag("following") + } NavigationView { HomeFeedContainerView(viewModel: libraryViewModel) @@ -80,7 +83,7 @@ struct LibraryTabView: View { .frame(height: 1) .frame(maxWidth: .infinity) } - CustomTabBar(selectedTab: $selectedTab) + CustomTabBar(selectedTab: $selectedTab, hideFollowingTab: hideFollowingTab) .padding(0) } .fullScreenCover(isPresented: $showExpandedAudioPlayer) { diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/FiltersView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/FiltersView.swift index 39ebe6966..66214abf9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/FiltersView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/FiltersView.swift @@ -11,6 +11,7 @@ import Views @Published var networkError = false @Published var libraryFilters = [InternalFilter]() + @AppStorage("LibraryTabView::hideFollowingTab") var hideFollowingTab = false @AppStorage(UserDefaultKey.hideFeatureSection.rawValue) var hideFeatureSection = false func loadFilters(dataService: DataService) async { @@ -50,7 +51,8 @@ struct FiltersView: View { private var innerBody: some View { List { Section { - Toggle("Hide Feature Section", isOn: $viewModel.hideFeatureSection) + Toggle("Hide following tab", isOn: $viewModel.hideFollowingTab) + Toggle("Hide feature section", isOn: $viewModel.hideFeatureSection) } Section(header: Text("Saved Searches")) { diff --git a/apple/OmnivoreKit/Sources/App/Views/TabBar/CustomTabBar.swift b/apple/OmnivoreKit/Sources/App/Views/TabBar/CustomTabBar.swift index 5d32c63b9..4c489aeb8 100644 --- a/apple/OmnivoreKit/Sources/App/Views/TabBar/CustomTabBar.swift +++ b/apple/OmnivoreKit/Sources/App/Views/TabBar/CustomTabBar.swift @@ -3,9 +3,13 @@ import SwiftUI struct CustomTabBar: View { @Binding var selectedTab: String + let hideFollowingTab: Bool + var body: some View { HStack(spacing: 0) { - TabBarButton(key: "following", image: Image.tabFollowing, selectedTab: $selectedTab) + if !hideFollowingTab { + TabBarButton(key: "following", image: Image.tabFollowing, selectedTab: $selectedTab) + } TabBarButton(key: "inbox", image: Image.tabLibrary, selectedTab: $selectedTab) TabBarButton(key: "profile", image: Image.tabProfile, selectedTab: $selectedTab) } diff --git a/apple/OmnivoreKit/Sources/Utils/FirstFewWords.swift b/apple/OmnivoreKit/Sources/Utils/FirstFewWords.swift new file mode 100644 index 000000000..4d752dc4e --- /dev/null +++ b/apple/OmnivoreKit/Sources/Utils/FirstFewWords.swift @@ -0,0 +1,31 @@ +// +// File.swift +// +// +// Created by Jackson Harper on 12/8/23. +// + +import Foundation +import NaturalLanguage + +public func extractFirstFewWords(_ title: String) -> String { + let languageRecognizer = NLLanguageRecognizer() + + languageRecognizer.processString(title) + let language = languageRecognizer.dominantLanguage ?? NLLanguage.english + + let tokenizer = NLTokenizer(unit: .word) + tokenizer.setLanguage(language) + tokenizer.string = title + + var words: [String] = [] + tokenizer.enumerateTokens(in: title.startIndex ..< title.endIndex) { range, _ in + let word = String(title[range]) + words.append(word) + return true + } + + print("WORDS: ", words) + let truncatedTitle = words.prefix(2).joined(separator: " ") + return truncatedTitle +} diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift index af29d25bb..79a97b8df 100644 --- a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift +++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift @@ -44,6 +44,7 @@ public extension Color { static var themeSolidBackground: Color { Color("_themeSolidBackground", bundle: .module) } static var thBorderColor: Color { Color("thBorderColor", bundle: .module) } static var thLibrarySeparator: Color { Color("thLibrarySeparator", bundle: .module) } + static var thLightWhiteGrey: Color { Color("_themeLightWhiteGrey", bundle: .module) } static var thFeatureSeparator: Color { Color("featureSeparator", bundle: .module) } diff --git a/apple/OmnivoreKit/Sources/Views/Colors/ThemeColors.xcassets/_themeLightWhiteGrey.colorset/Contents.json b/apple/OmnivoreKit/Sources/Views/Colors/ThemeColors.xcassets/_themeLightWhiteGrey.colorset/Contents.json new file mode 100644 index 000000000..479128a59 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Colors/ThemeColors.xcassets/_themeLightWhiteGrey.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xED", + "green" : "0xED", + "red" : "0xED" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xED", + "green" : "0xED", + "red" : "0xED" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift index 17e4a1396..c45a7b31e 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift @@ -107,18 +107,26 @@ public struct GridCard: View { .cornerRadius(5) } + var fallbackFont: Font { + if let uifont = UIFont(name: "Futura Bold", size: 16) { + return Font(uifont) + } + return Font.system(size: 16) + } + var fallbackImage: some View { GeometryReader { geo in HStack { - Text(item.unwrappedTitle.prefix(1)) - .font(Font.system(size: 128, weight: .bold)) - .offset(CGSize(width: -48, height: 12)) - .frame(alignment: .bottomLeading) - .foregroundColor(Gradient.randomColor(str: item.unwrappedTitle, offset: 1)) + Text(item.unwrappedTitle) + .font(fallbackFont) + .frame(alignment: .center) + .multilineTextAlignment(.leading) + .lineLimit(2) + .padding(10) + .foregroundColor(Color.themeMiddleGray) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Gradient.randomColor(str: item.unwrappedTitle, offset: 0)) - .background(LinearGradient(gradient: Gradient(fromStr: item.unwrappedTitle)!, startPoint: .top, endPoint: .bottom)) + .background(Color.thLightWhiteGrey) .frame(width: geo.size.width, height: geo.size.height) } } diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryFeatureCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryFeatureCard.swift index 35913ceff..4f8d6832e 100644 --- a/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryFeatureCard.swift +++ b/apple/OmnivoreKit/Sources/Views/FeedItem/LibraryFeatureCard.swift @@ -66,17 +66,25 @@ public struct LibraryFeatureCard: View { .cornerRadius(5) } + var fallbackFont: Font { + if let uifont = UIFont(name: "Futura Bold", size: 16) { + return Font(uifont) + } + return Font.system(size: 16) + } + var fallbackImage: some View { HStack { - Text(item.unwrappedTitle.prefix(1)) - .font(Font.system(size: 128, weight: .bold)) - .offset(CGSize(width: -48, height: 12)) - .frame(alignment: .bottomLeading) - .foregroundColor(Gradient.randomColor(str: item.unwrappedTitle, offset: 1)) + Text(item.unwrappedTitle) + .font(fallbackFont) + .frame(alignment: .center) + .multilineTextAlignment(.leading) + .lineLimit(2) + .padding(10) + .foregroundColor(Color.themeMiddleGray) } .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Gradient.randomColor(str: item.unwrappedTitle, offset: 0)) - .background(LinearGradient(gradient: Gradient(fromStr: item.unwrappedTitle)!, startPoint: .top, endPoint: .bottom)) + .background(Color.thLightWhiteGrey) .frame(width: 146, height: 90) } diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.swift b/apple/OmnivoreKit/Sources/Views/Images/Images.swift index d66deb6f1..eb8817b63 100644 --- a/apple/OmnivoreKit/Sources/Views/Images/Images.swift +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.swift @@ -21,6 +21,10 @@ public extension Image { static var readerSettings: Image { Image("reader-settings", bundle: .module) } static var utilityMenu: Image { Image("utility-menu", bundle: .module) } + static var addLink: Image { Image("add-link", bundle: .module) } + static var selectMultiple: Image { Image("select-multiple", bundle: .module) } + static var magnifyingGlass: Image { Image("magnifying-glass", bundle: .module) } + static var archive: Image { Image("archive", bundle: .module) } static var unarchive: Image { Image("unarchive", bundle: .module) } static var remove: Image { Image("remove", bundle: .module) } diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/add-link.imageset/Contents.json b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/add-link.imageset/Contents.json new file mode 100644 index 000000000..e343db9c2 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/add-link.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "add-link.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/add-link.imageset/add-link.svg b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/add-link.imageset/add-link.svg new file mode 100644 index 000000000..b4d8e3edc --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/add-link.imageset/add-link.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/magnifying-glass.imageset/Contents.json b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/magnifying-glass.imageset/Contents.json new file mode 100644 index 000000000..1da65d1a1 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/magnifying-glass.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "magnifying-glass.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/magnifying-glass.imageset/magnifying-glass.svg b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/magnifying-glass.imageset/magnifying-glass.svg new file mode 100644 index 000000000..65cf1cce7 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/magnifying-glass.imageset/magnifying-glass.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/select-multiple.imageset/Contents.json b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/select-multiple.imageset/Contents.json new file mode 100644 index 000000000..fe3fde928 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/select-multiple.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "multiple-select.svg", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/select-multiple.imageset/multiple-select.svg b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/select-multiple.imageset/multiple-select.svg new file mode 100644 index 000000000..66cf30494 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Views/Images/Images.xcassets/select-multiple.imageset/multiple-select.svg @@ -0,0 +1,11 @@ + + + + + + + + + + +