Skip to content
This repository has been archived by the owner on Sep 5, 2023. It is now read-only.

Commit

Permalink
Full product edition (#284)
Browse files Browse the repository at this point in the history
* updated carthage deps for xcode_10_2

* Allow edition of product using the same view as create. #190

* Accept int or string for nova_group

* edit product vc title

* progress hud while loading product from history

* handle modifier for nutriment item

* fixed cartfile resolved

* allow nutriment input modifiers

* product name and ingredients are now translated and switched when language is switched

* saving translated name and ingrdedients list
  • Loading branch information
philippeauriach authored and teolemon committed May 20, 2019
1 parent cbd8aea commit 18f3439
Show file tree
Hide file tree
Showing 28 changed files with 319 additions and 85 deletions.
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ github "httpswift/swifter" "bdfd5f5d9ef1137ee034287e06f14e1b8794beb3"
github "kishikawakatsumi/KeychainAccess" "v3.2.0"
github "marmelroy/Zip" "1.1.0"
github "onevcat/Kingfisher" "5.3.1"
github "philippeauriach/ImageViewer" "3a396dd97371e707183a5c9359d74ec47e3db173"
github "Tovkal/ImageViewer" "0.1.3"
github "realm/realm-cocoa" "v3.14.1"
github "rzulkoski/AlamofireObjectMapper" "c439696cdb5018ccbd9c84dde0552e7a819bf829"
github "scenee/FloatingPanel" "v1.3.5"
Expand Down
4 changes: 4 additions & 0 deletions OpenFoodFacts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
3000855E2207A3A500DC3896 /* TaxonomiesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3000855D2207A3A500DC3896 /* TaxonomiesService.swift */; };
300085612207AC0700DC3896 /* Additive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 300085602207AC0700DC3896 /* Additive.swift */; };
300085632208777900DC3896 /* OFFUrlsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 300085622208777900DC3896 /* OFFUrlsHelper.swift */; };
3006E07E2264934900618FB6 /* IntTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3006E07D2264934900618FB6 /* IntTransform.swift */; };
3010E25F222EF42B00313ADB /* InitialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3010E25E222EF42B00313ADB /* InitialView.swift */; };
302525E92223044E00C2C830 /* AllergensAlertsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302525E72223044E00C2C830 /* AllergensAlertsTableViewController.swift */; };
302525EC222307A200C2C830 /* RealmUserPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302525EB222307A200C2C830 /* RealmUserPreferences.swift */; };
Expand Down Expand Up @@ -302,6 +303,7 @@
3000855D2207A3A500DC3896 /* TaxonomiesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaxonomiesService.swift; sourceTree = "<group>"; };
300085602207AC0700DC3896 /* Additive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Additive.swift; sourceTree = "<group>"; };
300085622208777900DC3896 /* OFFUrlsHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OFFUrlsHelper.swift; sourceTree = "<group>"; };
3006E07D2264934900618FB6 /* IntTransform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntTransform.swift; sourceTree = "<group>"; };
3010E25E222EF42B00313ADB /* InitialView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialView.swift; sourceTree = "<group>"; };
302525E72223044E00C2C830 /* AllergensAlertsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllergensAlertsTableViewController.swift; sourceTree = "<group>"; };
302525EB222307A200C2C830 /* RealmUserPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmUserPreferences.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1390,6 +1392,7 @@
95781BB71EC3A898003E3256 /* ArrayTransform.swift */,
95781BB81EC3A898003E3256 /* DoubleTransform.swift */,
95781BB91EC3A898003E3256 /* TagTransform.swift */,
3006E07D2264934900618FB6 /* IntTransform.swift */,
);
path = Transforms;
sourceTree = "<group>";
Expand Down Expand Up @@ -2341,6 +2344,7 @@
7437FBA52036F1F800D107B6 /* CreditsViewController.swift in Sources */,
95781BBD1EC3A898003E3256 /* OFFWriteAPI.swift in Sources */,
958F33421F36196D005269C5 /* IngredientsFormTableViewController.swift in Sources */,
3006E07E2264934900618FB6 /* IntTransform.swift in Sources */,
957CBEBE1F33B2FE00A1B398 /* Form.swift in Sources */,
958F334B1F3777D7005269C5 /* NutritionTableFormTableViewController.swift in Sources */,
95F14C8B1EDCADD600D99115 /* RotatingProcessor.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

private func configureRealm() {
let config = Realm.Configuration(
schemaVersion: 25
schemaVersion: 29
)

Realm.Configuration.defaultConfiguration = config
Expand Down
15 changes: 15 additions & 0 deletions Sources/Helpers/operators.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,18 @@ func + (left: NSAttributedString, right: NSAttributedString) -> NSAttributedStri
result.append(right)
return result
}

extension String {
func matches(for regex: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: self, range: NSRange(self.startIndex..., in: self))
return results.map {
String(self[Range($0.range, in: self)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}
1 change: 1 addition & 0 deletions Sources/Localization/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"product-add.placeholder.quantity" = "Quantité";

"product-add.title" = "Nouveau produit";
"product-add.title-edit" = "Modifier le produit";
"product-add.titles.barcode" = "Code barre";
"product-add.titles.top-explainations" = "Ce produit n'est pas encore dans la base de données Open Food Facts. Pourriez-vous prendre quelques photos du produit, de son code-barre, de la liste des ingrédients et des informations nutritionnelles afin de l'ajouter à la base ? 

Merci par avance.";
"product-add.titles.license-explaination" = "Note : les photos que vous envoyées sont publiées sous la license libre Creative Commons Attribution and ShareAlike.";
Expand Down
22 changes: 6 additions & 16 deletions Sources/Models/API/Nutriments/NutrimentItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ struct NutrimentItem {
var per100g: Double?
var perServing: Double?
var unit: String?
var modifier: String?
var value: Double?
var isMainItem: Bool // Whether this item is the main item in a group, used to hightlight. Like Fat in the Fats group.

// Json keys
let nameKey: String
let per100gKey: String
let servingKey: String
let unitKey: String
let valueKey: String

let localized: InfoRowKey

Expand All @@ -40,16 +37,13 @@ struct NutrimentItem {

init?(nameKey: String, map: Map, localized: InfoRowKey, isMainItem mainItem: Bool = false) {
self.nameKey = nameKey
self.per100gKey = "\(nameKey)_100g"
self.servingKey = "\(nameKey)_serving"
self.unitKey = "\(nameKey)_unit"
self.valueKey = "\(nameKey)_value"

self.total <- (map[nameKey], DoubleTransform())
self.per100g <- (map[per100gKey], DoubleTransform())
self.perServing <- (map[servingKey], DoubleTransform())
self.unit <- map[unitKey]
self.value <- (map[valueKey], DoubleTransform())
self.per100g <- (map["\(nameKey)_100g"], DoubleTransform())
self.perServing <- (map["\(nameKey)_serving"], DoubleTransform())
self.unit <- map["\(nameKey)_unit"]
self.modifier <- map["\(nameKey)_modifier"]
self.value <- (map["\(nameKey)_value"], DoubleTransform())
self.isMainItem = mainItem

self.localized = localized
Expand All @@ -62,10 +56,6 @@ struct NutrimentItem {

init?(nameKey: String, localized: InfoRowKey) {
self.nameKey = nameKey
self.per100gKey = "\(nameKey)_100g"
self.servingKey = "\(nameKey)_serving"
self.unitKey = "\(nameKey)_unit"
self.valueKey = "\(nameKey)_value"
self.isMainItem = false

self.localized = localized
Expand Down
13 changes: 13 additions & 0 deletions Sources/Models/API/Nutriments/Nutriments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,17 @@ struct Nutriments: Mappable {
carbonFootprintUnit <- map[OFFJson.CarbonFootprintUnitKey]
}
// swiftlint:enable function_body_length

func allItems() -> [NutrimentItem] {
var results: [NutrimentItem?] = [NutrimentItem?]()
results.append(energy)
results.append(contentsOf: fats)
results.append(contentsOf: carbohydrates)
results.append(fiber)
results.append(contentsOf: proteins)
results.append(contentsOf: [salt, sodium, alcohol])
results.append(contentsOf: vitamins)
results.append(contentsOf: minerals)
return results.compactMap { $0 }
}
}
8 changes: 7 additions & 1 deletion Sources/Models/API/OFFReadAPIkeysJSON.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ struct OFFJson {
static let KeywordsKey = "_keywords"
static let RevKey = "rev"
// added for decoding of languages
static let LanguageCodesKey = "languages_codes"
static let LanguageCodesKey = "languages_codes"
static let ProductNameLanguagesKey = "product_name_languages"
static let GenericNameLanguagesKey = "generic_name_languages"
static let IngredientsLanguagesKey = "ingredients_text_languages"
static let EditorsKey = "editors"
static let InterfaceVersionCreatedKey = "interface_version_created"
static let EmbCodesKey = "emb_codes"
Expand Down Expand Up @@ -351,6 +354,9 @@ struct OFFJson {
OFFJson.LabelsHierarchyKey,
OFFJson.LabelsTagsKey,
OFFJson.LangKey,
OFFJson.ProductNameLanguagesKey,
OFFJson.GenericNameLanguagesKey,
OFFJson.IngredientsLanguagesKey,
OFFJson.LanguageCodesKey,
OFFJson.LastModifiedByKey,
OFFJson.LastModifiedTKey,
Expand Down
15 changes: 5 additions & 10 deletions Sources/Models/API/Product.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ struct Product: Mappable {
var categories: [String]?
var categoriesTags: [String]?
var nutriscore: String?
var novaGroup: String?
var novaGroup: Int?
var manufacturingPlaces: String?
var origins: String?
var labels: [String]?
Expand Down Expand Up @@ -247,7 +247,7 @@ struct Product: Mappable {
categories <- (map[OFFJson.CategoriesKey], ArrayTransform())
categoriesTags <- (map[OFFJson.CategoriesTagsKey])
nutriscore <- map[OFFJson.NutritionGradesKey]
novaGroup <- map[OFFJson.NovaGroupKey]
novaGroup <- (map[OFFJson.NovaGroupKey], IntTransform())
manufacturingPlaces <- map[OFFJson.ManufacturingPlacesKey]
origins <- map[OFFJson.OriginsKey]
labels <- (map[OFFJson.LabelsKey], ArrayTransform())
Expand Down Expand Up @@ -276,14 +276,9 @@ struct Product: Mappable {

// try to extract all language specific fields

// guard let validLanguageCodes = languageCodes else { return }

for languageCode in Locale.preferredLanguageCodes {

names[languageCode] <- map[OFFJson.ProductNameKey + OFFJson.KeySeparator + languageCode]
genericNames[languageCode] <- map[OFFJson.GenericNameKey + OFFJson.KeySeparator + languageCode]
ingredients[languageCode] <- map[OFFJson.IngredientsTextKey + OFFJson.KeySeparator + languageCode]
}
names <- map[OFFJson.ProductNameLanguagesKey]
genericNames <- map[OFFJson.GenericNameLanguagesKey]
ingredients <- map[OFFJson.IngredientsLanguagesKey]
}

func matchedLanguageCode(codes:[String]) -> String? {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Models/DataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class DataManager: DataManagerProtocol {

var params = [String: Any]()
nutritionTable.forEach { (item) in
params["nutriment_\(item.code)"] = item.value
params["nutriment_\(item.code)"] = (item.modifier ?? "") + "\(item.value)"
params["nutriment_\(item.code)_unit"] = item.unit
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Models/HistoryItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class HistoryItem: Object {
@objc dynamic var imageUrl: String?
@objc dynamic var timestamp = Date()
@objc dynamic var nutriscore: String?
@objc dynamic var novagroup: String?
let novaGroup = RealmOptional<Int>()

var age: Age {
let days: Double = 1 * 60 * 60 * 24
Expand Down
2 changes: 1 addition & 1 deletion Sources/Models/PersistenceManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class PersistenceManager: PersistenceManagerProtocol {
item.quantity = product.quantity
item.imageUrl = product.imageUrl
item.nutriscore = product.nutriscore
item.novagroup = product.novaGroup
item.novaGroup.value = product.novaGroup
item.timestamp = Date()

if let brands = product.brands, !brands.isEmpty {
Expand Down
1 change: 1 addition & 0 deletions Sources/Models/Realm/RealmPendingUploadItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import RealmSwift

internal class RealmPendingUploadNutrimentItem: Object {
@objc dynamic var code: String = ""
@objc dynamic var modifier: String? = nil
@objc dynamic var value: Double = 0
@objc dynamic var unit: String = ""
}
Expand Down
35 changes: 35 additions & 0 deletions Sources/Models/Transforms/IntTransform.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// IntTransform.swift
// OpenFoodFacts
//
// Created by Philippe Auriach on 15/04/2019.
// Copyright © 2019 Andrés Pizá Bückmann. All rights reserved.
//

import Foundation
import ObjectMapper

// The API sometimes returns a value as String and sometimes as Int, this transform handles this so we always have a Int

public class IntTransform: TransformType {
public typealias Object = Int
public typealias JSON = String

public func transformFromJSON(_ value: Any?) -> Object? {
if let value = value as? String, let intValue = Int(value) {
return intValue
} else if let value = value as? Int {
return value
}

return nil
}

public func transformToJSON(_ value: Object?) -> JSON? {
if let value = value {
return String(value)
}

return nil
}
}
6 changes: 6 additions & 0 deletions Sources/Models/Transforms/TagTransform.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ extension Array where Element: Tag {
func chooseForCurrentLanguage(defaultToFirst: Bool = false) -> Tag? {
return Tag.choose(inTags: self, forLanguageCode: nil, defaultToFirst: defaultToFirst)
}
func choose(forLanguageCode: String, defaultToFirst: Bool = false) -> Tag? {
return Tag.choose(inTags: self, forLanguageCode: forLanguageCode, defaultToFirst: defaultToFirst)
}
}

extension List where Element: Tag {
func chooseForCurrentLanguage(defaultToFirst: Bool = false) -> Tag? {
return Array(self).chooseForCurrentLanguage(defaultToFirst: defaultToFirst)
}
func choose(forLanguageCode: String, defaultToFirst: Bool = false) -> Tag? {
return Array(self).choose(forLanguageCode: forLanguageCode, defaultToFirst: defaultToFirst)
}
}

public class TagTransform: TransformType {
Expand Down
3 changes: 2 additions & 1 deletion Sources/Models/ViewModel/PictureViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct PictureViewModel {
let imageType: ImageType
let text: String?
var image: UIImage?
var imageUrl: String?
let uploadedPictureText: String?
var isUploading: Bool = false
}
Expand All @@ -32,6 +33,6 @@ extension PictureViewModel {
uploadedPictureText = "product.images.took-picture.nutrition".localized
}

self.init(imageType: imageType, text: text, image: nil, uploadedPictureText: uploadedPictureText, isUploading: false)
self.init(imageType: imageType, text: text, image: nil, imageUrl: nil, uploadedPictureText: uploadedPictureText, isUploading: false)
}
}
12 changes: 12 additions & 0 deletions Sources/Network/ProductService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,18 @@ extension ProductService {
func postProduct(_ product: Product, rawParameters: [String: Any]?, onSuccess: @escaping () -> Void, onError: @escaping (Error) -> Void) {
var params = product.toJSON()

// handle translated fields
if let lang = product.lang {
if let productName = product.names[lang] {
params["product_name"] = nil
params["product_name_\(lang)"] = productName
}
if let productIngredients = product.ingredients[lang] {
params["ingredients_text"] = nil
params["ingredients_text_\(lang)"] = productIngredients
}
}

if let rawParameters = rawParameters {
params.merge(rawParameters) { $1 }
}
Expand Down
14 changes: 11 additions & 3 deletions Sources/ViewControllers/Products/Add/EditNutritiveValueView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,17 @@ class EditNutritiveValueView: UIView {
}
}

func getInputValue() -> Double? {
if let txt = inputTextField.text?.replacingOccurrences(of: ",", with: ".") {
return Double(txt)
/// if fromString is nil, then it will use inputTextField.text
func getInputValue(fromString: String? = nil) -> (modifier: String?, value: Double)? {
let text = fromString ?? inputTextField.text

var modifier: String?
if let firstChar = text?.first, ["<", ">", "~"].contains(firstChar) {
modifier = "\(firstChar)"
}
if let txt = text?.replacingOccurrences(of: "[<>~]", with: "", options: .regularExpression).replacingOccurrences(of: ",", with: "."),
let doubleValue = Double(txt) {
return (modifier, doubleValue)
}
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class PictureTableViewController: TakePictureViewController {
@IBOutlet weak var tableView: UITableView!
var pictures = [PictureViewModel]()
weak var delegate: PictureTableViewControllerDelegate?
var productToEdit: Product?

override func viewDidLoad() {
tableView.isScrollEnabled = false
Expand All @@ -24,6 +25,9 @@ class PictureTableViewController: TakePictureViewController {
pictures.append(PictureViewModel(imageType: .ingredients))
pictures.append(PictureViewModel(imageType: .nutrition))

if let product = self.productToEdit {
fillForm(withProduct: product)
}
if let barcode = self.barcode, let pendingUploadItem = dataManager.getItemPendingUpload(forBarcode: barcode) {
fillForm(withPendingUploadItem: pendingUploadItem)
}
Expand All @@ -35,6 +39,27 @@ class PictureTableViewController: TakePictureViewController {
})
}

fileprivate func fillForm(withProduct product: Product) {
if let image = product.frontImageSmallUrl ?? product.frontImageUrl {
if let index = index(forImageType: .front) {
pictures[index].imageUrl = image
tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}
}
if let image = product.ingredientsImageUrl {
if let index = index(forImageType: .ingredients) {
pictures[index].imageUrl = image
tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}
}
if let image = product.nutritionTableImage {
if let index = index(forImageType: .nutrition) {
pictures[index].imageUrl = image
tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
}
}
}

fileprivate func fillForm(withPendingUploadItem pendingUploadItem: PendingUploadItem) {
if let image = pendingUploadItem.frontImage {
if let index = index(forImageType: .front) {
Expand Down
Loading

0 comments on commit 18f3439

Please sign in to comment.