Merge pull request #799 from omnivore-app/feature/edit-title-ios

Edit title and description [iOS]
This commit is contained in:
Satindar Dhillon 2022-06-14 14:29:08 -07:00 committed by GitHub
commit 7a235ae70b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 242 additions and 23 deletions

View file

@ -1229,7 +1229,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 51;
CURRENT_PROJECT_VERSION = 52;
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = InfoPlists/ShareExtensionMac.plist;
@ -1239,7 +1239,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.ShareExtension-Mac";
@ -1260,7 +1260,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 51;
CURRENT_PROJECT_VERSION = 52;
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = InfoPlists/ShareExtensionMac.plist;
@ -1270,7 +1270,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.ShareExtension-Mac";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -1341,7 +1341,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 51;
CURRENT_PROJECT_VERSION = 52;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_HARDENED_RUNTIME = YES;
@ -1352,7 +1352,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
@ -1375,7 +1375,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 51;
CURRENT_PROJECT_VERSION = 52;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_HARDENED_RUNTIME = YES;
@ -1386,7 +1386,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -1441,7 +1441,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
PRODUCT_NAME = Omnivore;
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1473,7 +1473,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
@ -1512,7 +1512,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"-framework",
@ -1538,7 +1538,7 @@
CODE_SIGN_ENTITLEMENTS = "Entitlements/SafariExtension-Mac.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 51;
CURRENT_PROJECT_VERSION = 52;
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
@ -1551,7 +1551,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
@ -1576,7 +1576,7 @@
CODE_SIGN_ENTITLEMENTS = "Entitlements/SafariExtension-Mac.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 51;
CURRENT_PROJECT_VERSION = 52;
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
@ -1589,7 +1589,7 @@
"@executable_path/../../../../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"-framework",
@ -1674,7 +1674,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension";
PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos;
@ -1728,7 +1728,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
PRODUCT_NAME = Omnivore;
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1756,7 +1756,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.8.0;
MARKETING_VERSION = 1.9.0;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension";
PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos;

View file

@ -55,6 +55,9 @@ import Views
.sheet(item: $viewModel.itemUnderLabelEdit) { item in
ApplyLabelsView(mode: .item(item), onSave: nil)
}
.sheet(item: $viewModel.itemUnderTitleEdit) { item in
LinkedItemTitleEditView(item: item)
}
.toolbar {
ToolbarItem(placement: .barTrailing) {
Button("", action: {})
@ -230,6 +233,10 @@ import Views
viewModel: viewModel
)
.contextMenu {
Button(
action: { viewModel.itemUnderTitleEdit = item },
label: { Label("Edit Title/Description", systemImage: "textbox") }
)
Button(
action: { viewModel.itemUnderLabelEdit = item },
label: { Label("Edit Labels", systemImage: "tag") }
@ -344,6 +351,8 @@ import Views
confirmationShown = true
case .editLabels:
viewModel.itemUnderLabelEdit = item
case .editTitle:
viewModel.itemUnderTitleEdit = item
}
}

View file

@ -26,6 +26,10 @@ import Views
viewModel: viewModel
)
.contextMenu {
Button(
action: { viewModel.itemUnderTitleEdit = item },
label: { Label("Edit Title/Description", systemImage: "textbox") }
)
Button(
action: { viewModel.itemUnderLabelEdit = item },
label: { Label("Edit Labels", systemImage: "tag") }

View file

@ -14,6 +14,7 @@ import Views
@Published var isLoading = false
@Published var showPushNotificationPrimer = false
@Published var itemUnderLabelEdit: LinkedItem?
@Published var itemUnderTitleEdit: LinkedItem?
@Published var searchTerm = ""
@Published var selectedLabels = [LinkedItemLabel]()
@Published var negatedLabels = [LinkedItemLabel]()

View file

@ -119,6 +119,7 @@ struct LinkItemDetailView: View {
static let navBarHeight = 50.0
@ObservedObject private var viewModel: LinkItemDetailViewModel
@State private var showFontSizePopover = false
@State private var showTitleEdit = false
@State private var navBarVisibilityRatio = 1.0
@State private var showDeleteConfirmation = false
@ -206,6 +207,10 @@ struct LinkItemDetailView: View {
Menu(
content: {
Group {
Button(
action: { showTitleEdit = true },
label: { Label("Edit Title/Description", systemImage: "textbox") }
)
Button(
action: { viewModel.handleArchiveAction(dataService: dataService) },
label: {
@ -240,6 +245,11 @@ struct LinkItemDetailView: View {
}
Button("Cancel", role: .cancel, action: {})
}
.sheet(isPresented: $showTitleEdit) {
if let item = viewModel.item {
LinkedItemTitleEditView(item: item)
}
}
}
#if os(iOS)

View file

@ -0,0 +1,90 @@
import Models
import Services
import SwiftUI
import Views
@MainActor final class LinkedItemTitleEditViewModel: ObservableObject {
@Published var title = ""
@Published var description = ""
func load(item: LinkedItem) {
title = item.unwrappedTitle
description = item.descriptionText ?? ""
}
func submit(dataService: DataService, item: LinkedItem) {
dataService.updateLinkedItemTitleAndDescription(
itemID: item.unwrappedID,
title: title,
description: description
)
}
}
struct LinkedItemTitleEditView: View {
@EnvironmentObject var dataService: DataService
@Environment(\.presentationMode) private var presentationMode
@StateObject var viewModel = LinkedItemTitleEditViewModel()
let item: LinkedItem
var editForm: some View {
ScrollView(showsIndicators: false) {
VStack(alignment: .center, spacing: 16) {
VStack(alignment: .leading, spacing: 6) {
Text("Title")
.font(.appFootnote)
.foregroundColor(.appGrayTextContrast)
TextField("", text: $viewModel.title)
.textFieldStyle(StandardTextFieldStyle(textColor: .appGrayTextContrast))
}
VStack(alignment: .leading, spacing: 6) {
Text("Description")
.font(.appFootnote)
.foregroundColor(.appGrayTextContrast)
TextEditor(text: $viewModel.description)
.lineSpacing(6)
.accentColor(.appGraySolid)
.foregroundColor(.appGrayTextContrast)
.font(.appBody)
.padding(12)
.background(
RoundedRectangle(cornerRadius: 8)
.strokeBorder(Color.appGrayBorder, lineWidth: 1)
.background(RoundedRectangle(cornerRadius: 8).fill(Color.systemBackground))
)
.frame(height: 200)
}
}
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
var body: some View {
NavigationView {
editForm
.navigationTitle("Edit Title and Description")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .barTrailing) {
Button(
action: {
viewModel.submit(dataService: dataService, item: item)
presentationMode.wrappedValue.dismiss()
},
label: { Text("Save").foregroundColor(.appGrayTextContrast) }
)
}
ToolbarItem(placement: .barLeading) {
Button(
action: { presentationMode.wrappedValue.dismiss() },
label: { Text("Cancel").foregroundColor(.appGrayTextContrast) }
)
}
}
}
.task { viewModel.load(item: item) }
}
}

View file

@ -10,6 +10,7 @@ import WebKit
@State private var showPreferencesPopover = false
@State private var showLabelsModal = false
@State private var showTitleEdit = false
@State var showHighlightAnnotationModal = false
@State var safariWebLink: SafariWebLink?
@State private var navBarVisibilityRatio = 1.0
@ -82,6 +83,10 @@ import WebKit
Menu(
content: {
Group {
Button(
action: { showTitleEdit = true },
label: { Label("Edit Title/Description", systemImage: "textbox") }
)
Button(
action: { showLabelsModal = true },
label: { Label("Edit Labels", systemImage: "tag") }
@ -130,6 +135,9 @@ import WebKit
.sheet(isPresented: $showLabelsModal) {
ApplyLabelsView(mode: .item(item), onSave: { _ in showLabelsModal = false })
}
.sheet(isPresented: $showTitleEdit) {
LinkedItemTitleEditView(item: item)
}
}
var body: some View {

View file

@ -111,7 +111,9 @@ public extension LinkedItem {
inContext context: NSManagedObjectContext,
newReadingProgress: Double? = nil,
newAnchorIndex: Int? = nil,
newIsArchivedValue: Bool? = nil
newIsArchivedValue: Bool? = nil,
newTitle: String? = nil,
newDescription: String? = nil
) {
context.perform {
if let newReadingProgress = newReadingProgress {
@ -126,6 +128,14 @@ public extension LinkedItem {
self.isArchived = newIsArchivedValue
}
if let newTitle = newTitle {
self.title = newTitle
}
if let newDescription = newDescription {
self.descriptionText = newDescription
}
guard context.hasChanges else { return }
self.updatedAt = Date()

View file

@ -0,0 +1,77 @@
import CoreData
import Foundation
import Models
import SwiftGraphQL
extension DataService {
public func updateLinkedItemTitleAndDescription(itemID: String, title: String, description: String) {
backgroundContext.perform { [weak self] in
guard let self = self else { return }
guard let linkedItem = LinkedItem.lookup(byID: itemID, inContext: self.backgroundContext) else { return }
linkedItem.update(
inContext: self.backgroundContext,
newTitle: title,
newDescription: description
)
// Send update to server
self.syncLinkedItemTitleAndDescription(
itemID: itemID,
objectID: linkedItem.objectID,
title: title,
description: description
)
}
}
func syncLinkedItemTitleAndDescription(
itemID: String,
objectID: NSManagedObjectID,
title: String,
description: String
) {
enum MutationResult {
case saved(title: String)
case error(errorMessage: String)
}
let selection = Selection<MutationResult, Unions.UpdatePageResult> {
try $0.on(
updatePageError: .init { .error(errorMessage: try $0.errorCodes().first.toString()) },
updatePageSuccess: .init {
.saved(title: try $0.updatedPage(selection: Selection.Article { try $0.title() }))
}
)
}
let mutation = Selection.Mutation {
try $0.updatePage(
input: .init(description: OptionalArgument(description), pageId: itemID, title: OptionalArgument(title)),
selection: selection
)
}
let path = appEnvironment.graphqlPath
let headers = networker.defaultHeaders
let context = backgroundContext
send(mutation, to: path, headers: headers) { result in
let data = try? result.get()
let syncStatus: ServerSyncStatus = data == nil ? .needsUpdate : .isNSync
context.perform {
guard let linkedItem = context.object(with: objectID) as? LinkedItem else { return }
linkedItem.serverSyncStatus = Int64(syncStatus.rawValue)
do {
try context.save()
logger.debug("LinkedItem updated succesfully")
} catch {
context.rollback()
logger.debug("Failed to update LinkedItem: \(error.localizedDescription)")
}
}
}
}
}

View file

@ -6,6 +6,7 @@ public enum GridCardAction {
case toggleArchiveStatus
case delete
case editLabels
case editTitle
}
public struct GridCard: View {
@ -43,6 +44,10 @@ public struct GridCard: View {
var contextMenuView: some View {
Group {
Button(
action: { menuActionHandler(.editTitle) },
label: { Label("Edit Title/Description", systemImage: "textbox") }
)
Button(
action: { menuActionHandler(.editLabels) },
label: { Label("Edit Labels", systemImage: "tag") }

View file

@ -1,13 +1,18 @@
import SwiftUI
public struct StandardTextFieldStyle: TextFieldStyle {
public init() {}
let textColor: Color?
public init(textColor: Color = .appGrayText) {
self.textColor = textColor
}
// swiftlint:disable:next identifier_name
public func _body(configuration: TextField<_Label>) -> some View {
configuration
.textFieldStyle(PlainTextFieldStyle())
.multilineTextAlignment(.leading)
.foregroundColor(.appGrayText)
.foregroundColor(textColor)
.font(.appBody)
.padding(.vertical, 12)
.padding(.horizontal, 16)
@ -15,9 +20,9 @@ public struct StandardTextFieldStyle: TextFieldStyle {
}
public var border: some View {
RoundedRectangle(cornerRadius: 16)
RoundedRectangle(cornerRadius: 8)
.strokeBorder(Color.appGrayBorder, lineWidth: 1)
.background(RoundedRectangle(cornerRadius: 16).fill(Color.systemBackground))
.background(RoundedRectangle(cornerRadius: 8).fill(Color.systemBackground))
}
}