diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/RecommendToView.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/RecommendToView.swift
index 24b624a91..20d737d03 100644
--- a/apple/OmnivoreKit/Sources/App/Views/WebReader/RecommendToView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/RecommendToView.swift
@@ -10,6 +10,7 @@ import Views
@Published var selectedGroups = [String]()
@Published var isRunning = false
@Published var showError = false
+ @Published var note: String?
let pageID: String
@@ -34,7 +35,7 @@ import Views
isRunning = true
do {
- try await dataService.recommendPage(pageID: pageID, groupIDs: selectedGroups)
+ try await dataService.recommendPage(pageID: pageID, groupIDs: selectedGroups, note: note)
} catch {
showError = true
}
diff --git a/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents b/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents
index 9dbaf53aa..deb3afd4f 100644
--- a/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents
+++ b/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents
@@ -55,7 +55,7 @@
-
+
@@ -96,7 +96,8 @@
-
+
+
diff --git a/apple/OmnivoreKit/Sources/Models/CoreData/StorageProvider.swift b/apple/OmnivoreKit/Sources/Models/CoreData/StorageProvider.swift
index 6198ec852..12a31526a 100644
--- a/apple/OmnivoreKit/Sources/Models/CoreData/StorageProvider.swift
+++ b/apple/OmnivoreKit/Sources/Models/CoreData/StorageProvider.swift
@@ -21,6 +21,7 @@ public class PersistentContainer: NSPersistentContainer {
#endif
let appGroupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID)
let appGroupContainerURL = appGroupContainer?.appendingPathComponent("store.sqlite")
+ print("starting with sqlite container", appGroupContainerURL)
container.persistentStoreDescriptions.first!.url = appGroupContainerURL
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift b/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift
index 33c18d55a..22963144d 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift
@@ -684,7 +684,7 @@ extension Objects {
let readAt: [String: DateTime]
let readingProgressAnchorIndex: [String: Int]
let readingProgressPercent: [String: Double]
- let recommendedBy: [String: [Objects.Recommendation]]
+ let recommendations: [String: [Objects.Recommendation]]
let savedAt: [String: DateTime]
let savedByViewer: [String: Bool]
let shareInfo: [String: Objects.LinkShareInfo]
@@ -807,7 +807,7 @@ extension Objects.Article: Decodable {
if let value = try container.decode(Double?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
- case "recommendedBy":
+ case "recommendations":
if let value = try container.decode([Objects.Recommendation]?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
@@ -903,7 +903,7 @@ extension Objects.Article: Decodable {
readAt = map["readAt"]
readingProgressAnchorIndex = map["readingProgressAnchorIndex"]
readingProgressPercent = map["readingProgressPercent"]
- recommendedBy = map["recommendedBy"]
+ recommendations = map["recommendations"]
savedAt = map["savedAt"]
savedByViewer = map["savedByViewer"]
shareInfo = map["shareInfo"]
@@ -1282,9 +1282,9 @@ extension Fields where TypeLock == Objects.Article {
}
}
- func recommendedBy(selection: Selection) throws -> Type {
+ func recommendations(selection: Selection) throws -> Type {
let field = GraphQLField.composite(
- name: "recommendedBy",
+ name: "recommendations",
arguments: [],
selection: selection.selection
)
@@ -1292,7 +1292,7 @@ extension Fields where TypeLock == Objects.Article {
switch response {
case let .decoding(data):
- return try selection.decode(data: data.recommendedBy[field.alias!])
+ return try selection.decode(data: data.recommendations[field.alias!])
case .mocking:
return selection.mock()
}
@@ -13733,7 +13733,9 @@ extension Objects {
let __typename: TypeName = .recommendation
let id: [String: String]
let name: [String: String]
+ let note: [String: String]
let recommendedAt: [String: DateTime]
+ let user: [String: Objects.RecommendingUser]
enum TypeName: String, Codable {
case recommendation = "Recommendation"
@@ -13761,10 +13763,18 @@ extension Objects.Recommendation: Decodable {
if let value = try container.decode(String?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
+ case "note":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
case "recommendedAt":
if let value = try container.decode(DateTime?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
+ case "user":
+ if let value = try container.decode(Objects.RecommendingUser?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
default:
throw DecodingError.dataCorrupted(
DecodingError.Context(
@@ -13777,7 +13787,9 @@ extension Objects.Recommendation: Decodable {
id = map["id"]
name = map["name"]
+ note = map["note"]
recommendedAt = map["recommendedAt"]
+ user = map["user"]
}
}
@@ -13818,6 +13830,21 @@ extension Fields where TypeLock == Objects.Recommendation {
}
}
+ func note() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "note",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.note[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
func recommendedAt() throws -> DateTime {
let field = GraphQLField.leaf(
name: "recommendedAt",
@@ -13835,6 +13862,22 @@ extension Fields where TypeLock == Objects.Recommendation {
return DateTime.mockValue
}
}
+
+ func user(selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "user",
+ arguments: [],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return try selection.decode(data: data.user[field.alias!])
+ case .mocking:
+ return selection.mock()
+ }
+ }
}
extension Selection where TypeLock == Never, Type == Never {
@@ -14052,6 +14095,140 @@ extension Selection where TypeLock == Never, Type == Never {
typealias RecommendationGroup = Selection
}
+extension Objects {
+ struct RecommendingUser {
+ let __typename: TypeName = .recommendingUser
+ let name: [String: String]
+ let profileImageUrl: [String: String]
+ let userId: [String: String]
+ let username: [String: String]
+
+ enum TypeName: String, Codable {
+ case recommendingUser = "RecommendingUser"
+ }
+ }
+}
+
+extension Objects.RecommendingUser: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "name":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "profileImageUrl":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "userId":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "username":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ name = map["name"]
+ profileImageUrl = map["profileImageUrl"]
+ userId = map["userId"]
+ username = map["username"]
+ }
+}
+
+extension Fields where TypeLock == Objects.RecommendingUser {
+ func name() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "name",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.name[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+
+ func profileImageUrl() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "profileImageURL",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.profileImageUrl[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func userId() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "userId",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.userId[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+
+ func username() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "username",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.username[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias RecommendingUser = Selection
+}
+
extension Objects {
struct Reminder {
let __typename: TypeName = .reminder
@@ -15471,7 +15648,7 @@ extension Objects {
let readAt: [String: DateTime]
let readingProgressAnchorIndex: [String: Int]
let readingProgressPercent: [String: Double]
- let recommendedBy: [String: [Objects.Recommendation]]
+ let recommendations: [String: [Objects.Recommendation]]
let savedAt: [String: DateTime]
let shortId: [String: String]
let siteIcon: [String: String]
@@ -15584,7 +15761,7 @@ extension Objects.SearchItem: Decodable {
if let value = try container.decode(Double?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
- case "recommendedBy":
+ case "recommendations":
if let value = try container.decode([Objects.Recommendation]?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
@@ -15670,7 +15847,7 @@ extension Objects.SearchItem: Decodable {
readAt = map["readAt"]
readingProgressAnchorIndex = map["readingProgressAnchorIndex"]
readingProgressPercent = map["readingProgressPercent"]
- recommendedBy = map["recommendedBy"]
+ recommendations = map["recommendations"]
savedAt = map["savedAt"]
shortId = map["shortId"]
siteIcon = map["siteIcon"]
@@ -16011,9 +16188,9 @@ extension Fields where TypeLock == Objects.SearchItem {
}
}
- func recommendedBy(selection: Selection) throws -> Type {
+ func recommendations(selection: Selection) throws -> Type {
let field = GraphQLField.composite(
- name: "recommendedBy",
+ name: "recommendations",
arguments: [],
selection: selection.selection
)
@@ -16021,7 +16198,7 @@ extension Fields where TypeLock == Objects.SearchItem {
switch response {
case let .decoding(data):
- return try selection.decode(data: data.recommendedBy[field.alias!])
+ return try selection.decode(data: data.recommendations[field.alias!])
case .mocking:
return selection.mock()
}
@@ -30128,16 +30305,20 @@ extension InputObjects {
struct RecommendInput: Encodable, Hashable {
var groupIds: [String]
+ var note: OptionalArgument = .absent()
+
var pageId: String
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(groupIds, forKey: .groupIds)
+ if note.hasValue { try container.encode(note, forKey: .note) }
try container.encode(pageId, forKey: .pageId)
}
enum CodingKeys: String, CodingKey {
case groupIds
+ case note
case pageId
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/RecommendPage.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/RecommendPage.swift
index 6716460db..68f604817 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/RecommendPage.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/RecommendPage.swift
@@ -4,7 +4,7 @@ import Models
import SwiftGraphQL
public extension DataService {
- func recommendPage(pageID: String, groupIDs: [String]) async throws {
+ func recommendPage(pageID: String, groupIDs: [String], note: String?) async throws {
enum MutationResult {
case saved(taskNames: [String])
case error(errorMessage: String)
@@ -21,7 +21,7 @@ public extension DataService {
let mutation = Selection.Mutation {
try $0.recommend(
- input: .init(groupIds: groupIDs, pageId: pageID),
+ input: .init(groupIds: groupIDs, note: OptionalArgument(note), pageId: pageID),
selection: selection
)
}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift
index bf7f5f639..a9170e38e 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ArticleContentQuery.swift
@@ -44,7 +44,7 @@ extension DataService {
contentReader: try $0.contentReader().rawValue,
originalHtml: nil,
language: try $0.language(),
- recommendedBy: try $0.recommendedBy(selection: recommendationSelection.list.nullable) ?? [],
+ recommendations: try $0.recommendations(selection: recommendationSelection.list.nullable) ?? [],
labels: try $0.labels(selection: feedItemLabelSelection.list.nullable) ?? []
),
htmlContent: try $0.content(),
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift
index 6bb543079..51ded19f7 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/LinkedItemNetworkQuery.swift
@@ -224,12 +224,38 @@ extension DataService {
}
}
+let recommendingUserSelection = Selection.RecommendingUser {
+ do {
+ return InternalUserProfile(
+ userID: try $0.userId(),
+ name: try $0.name(),
+ username: try $0.username(),
+ profileImageURL: nil // try $0.profileImageUrl() ?? nil
+ )
+ } catch {
+ print("ERROR WITH recommendingUserSelection", error)
+ throw error
+ }
+}
+
let recommendationSelection = Selection.Recommendation {
- InternalRecommendation(
- id: try $0.id(),
- name: try $0.name(),
- recommendedAt: try $0.recommendedAt().value ?? Date()
- )
+ do {
+ let result = InternalRecommendation(
+ id: try $0.id(),
+ name: try $0.name(),
+ user: try $0.user(selection: recommendingUserSelection.nullable),
+ recommendedAt: try $0.recommendedAt().value ?? Date()
+ )
+ return result
+ } catch {
+ print("ERROR WITH recommendationSelection", error)
+ throw error
+ }
+}
+
+private func emptyrecommended() -> [InternalRecommendation] {
+ print("got the empty InternalRecommendation")
+ return []
}
private let libraryArticleSelection = Selection.Article {
@@ -257,7 +283,7 @@ private let libraryArticleSelection = Selection.Article {
contentReader: try $0.contentReader().rawValue,
originalHtml: nil,
language: try $0.language(),
- recommendedBy: try $0.recommendedBy(selection: recommendationSelection.list.nullable) ?? [],
+ recommendations: try $0.recommendations(selection: recommendationSelection.list.nullable) ?? emptyrecommended(),
labels: try $0.labels(selection: feedItemLabelSelection.list.nullable) ?? []
)
}
@@ -295,7 +321,7 @@ private let searchItemSelection = Selection.SearchItem {
contentReader: try $0.contentReader().rawValue,
originalHtml: nil,
language: try $0.language(),
- recommendedBy: try $0.recommendedBy(selection: recommendationSelection.list.nullable) ?? [],
+ recommendations: try $0.recommendations(selection: recommendationSelection.list.nullable) ?? [],
labels: try $0.labels(selection: feedItemLabelSelection.list.nullable) ?? []
)
}
diff --git a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalLinkedItem.swift b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalLinkedItem.swift
index 77ee4ae91..46827adf8 100644
--- a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalLinkedItem.swift
+++ b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalLinkedItem.swift
@@ -26,7 +26,7 @@ struct InternalLinkedItem {
let contentReader: String?
let originalHtml: String?
let language: String?
- let recommendedBy: [InternalRecommendation]
+ let recommendations: [InternalRecommendation]
var labels: [InternalLinkedItemLabel]
var isPDF: Bool {
@@ -73,12 +73,12 @@ struct InternalLinkedItem {
linkedItem.addToLabels(label.asManagedObject(inContext: context))
}
- if let existingRecommendation = linkedItem.recommendedBy {
- linkedItem.removeFromRecommendedBy(existingRecommendation)
+ if let existingRecommendation = linkedItem.recommendations {
+ linkedItem.removeFromRecommendations(existingRecommendation)
}
- for recommendation in recommendedBy {
- linkedItem.addToRecommendedBy(recommendation.asManagedObject(inContext: context))
+ for recommendation in recommendations {
+ linkedItem.addToRecommendations(recommendation.asManagedObject(inContext: context))
}
return linkedItem
@@ -142,7 +142,7 @@ extension JSONArticle {
contentReader: contentReader,
originalHtml: nil,
language: language,
- recommendedBy: [], // TODO:
+ recommendations: [], // TODO:
labels: []
)
diff --git a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalRecommendation.swift b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalRecommendation.swift
index c706d7014..80a33b0cc 100644
--- a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalRecommendation.swift
+++ b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalRecommendation.swift
@@ -2,10 +2,11 @@ import CoreData
import Foundation
import Models
-public struct InternalRecommendation: Encodable {
+public struct InternalRecommendation {
let id: String
let name: String
- let recommendedAt: Date?
+ let user: InternalUserProfile?
+ let recommendedAt: Date
func asManagedObject(inContext context: NSManagedObjectContext) -> Recommendation {
let existing = Recommendation.lookup(byID: id, inContext: context)
@@ -13,6 +14,7 @@ public struct InternalRecommendation: Encodable {
recommendation.id = id
recommendation.name = name
recommendation.recommendedAt = recommendedAt
+ recommendation.user = user?.asManagedObject(inContext: context)
return recommendation
}
@@ -27,6 +29,7 @@ public struct InternalRecommendation: Encodable {
return InternalRecommendation(
id: id,
name: name,
+ user: InternalUserProfile.makeSingle(recommendation.user),
recommendedAt: recommendedAt
)
}
diff --git a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalUserProfile.swift b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalUserProfile.swift
index 75a2924a8..53b656890 100644
--- a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalUserProfile.swift
+++ b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalUserProfile.swift
@@ -42,6 +42,22 @@ public struct InternalUserProfile: Identifiable {
return userProfile
}
+ public static func makeSingle(_ user: UserProfile?) -> InternalUserProfile? {
+ if let user = user,
+ let userID = user.userID,
+ let name = user.name,
+ let username = user.username
+ {
+ return InternalUserProfile(
+ userID: userID,
+ name: name,
+ username: username,
+ profileImageURL: user.profileImageURL
+ )
+ }
+ return nil
+ }
+
public static func make(_ users: NSSet?) -> [InternalUserProfile] {
users?
.compactMap { user in
diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift
index 1a7d04bd6..027bc4427 100644
--- a/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift
+++ b/apple/OmnivoreKit/Sources/Views/FeedItem/HomeFeedCardView.swift
@@ -87,15 +87,21 @@ public struct FeedCard: View {
#endif
}
- if let recommendedBy = item.recommendedBy, recommendedBy.count > 0 {
- let str = recommendedBy.reduce("") { str, item in
+ if let recommendations = item.recommendations, recommendations.count > 0 {
+ let byStr = recommendations.reduce("") { str, item in
+ if let item = item as? Recommendation, let name = item.user?.name {
+ return str + name
+ }
+ return str
+ }
+ let inStr = recommendations.reduce("") { str, item in
if let item = item as? Recommendation, let name = item.name {
return str + name
}
return str
}
HStack {
- Text("Recommended in \(str)")
+ Text("Recommended by \(byStr) in \(inStr)")
.font(.appCaption)
.frame(alignment: .leading)
Spacer()
diff --git a/packages/api/src/elastic/recommendation.ts b/packages/api/src/elastic/recommendation.ts
index 511b3fae1..924f444fe 100644
--- a/packages/api/src/elastic/recommendation.ts
+++ b/packages/api/src/elastic/recommendation.ts
@@ -13,19 +13,19 @@ export const addRecommendation = async (
url: page.url,
})
if (existingPage) {
- if (existingPage.recommendedBy?.includes(recommendation)) {
+ if (existingPage.recommendations?.includes(recommendation)) {
return existingPage._id
}
// update recommendedBy in the existing page
- const recommendedBy = (existingPage.recommendedBy || []).concat(
+ const recommendations = (existingPage.recommendations || []).concat(
recommendation
)
await updatePage(
existingPage.id,
{
- recommendedBy,
+ recommendations,
},
ctx
)
@@ -36,7 +36,7 @@ export const addRecommendation = async (
const newPage: Page = {
...page,
id: '',
- recommendedBy: [recommendation],
+ recommendations: [recommendation],
userId: ctx.uid,
readingProgressPercent: 0,
readingProgressAnchorIndex: 0,
diff --git a/packages/api/src/elastic/types.ts b/packages/api/src/elastic/types.ts
index ade5f5f1f..e4164b9c3 100644
--- a/packages/api/src/elastic/types.ts
+++ b/packages/api/src/elastic/types.ts
@@ -249,7 +249,7 @@ export interface Page {
readAt?: Date
listenedAt?: Date
wordsCount?: number
- recommendedBy?: Recommendation[]
+ recommendations?: Recommendation[]
}
export interface SearchItem {
@@ -282,7 +282,7 @@ export interface SearchItem {
wordsCount?: number
siteName?: string
siteIcon?: string
- recommendedBy?: Recommendation[]
+ recommendations?: Recommendation[]
}
const keys = ['_id', 'url', 'slug', 'userId', 'uploadFileId', 'state'] as const
diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts
index c68407531..8728bb795 100644
--- a/packages/api/src/generated/graphql.ts
+++ b/packages/api/src/generated/graphql.ts
@@ -110,7 +110,7 @@ export type Article = {
readAt?: Maybe;
readingProgressAnchorIndex: Scalars['Int'];
readingProgressPercent: Scalars['Float'];
- recommendedBy?: Maybe>;
+ recommendations?: Maybe>;
savedAt: Scalars['Date'];
savedByViewer?: Maybe;
shareInfo?: Maybe;
@@ -1804,7 +1804,7 @@ export type Recommendation = {
name: Scalars['String'];
note?: Maybe;
recommendedAt: Scalars['Date'];
- user: RecommendingUser;
+ user?: Maybe;
};
export type RecommendationGroup = {
@@ -2057,7 +2057,7 @@ export type SearchItem = {
readAt?: Maybe;
readingProgressAnchorIndex: Scalars['Int'];
readingProgressPercent: Scalars['Float'];
- recommendedBy?: Maybe>;
+ recommendations?: Maybe>;
savedAt: Scalars['Date'];
shortId?: Maybe;
siteIcon?: Maybe;
@@ -3889,7 +3889,7 @@ export type ArticleResolvers, ParentType, ContextType>;
readingProgressAnchorIndex?: Resolver;
readingProgressPercent?: Resolver;
- recommendedBy?: Resolver>, ParentType, ContextType>;
+ recommendations?: Resolver>, ParentType, ContextType>;
savedAt?: Resolver;
savedByViewer?: Resolver, ParentType, ContextType>;
shareInfo?: Resolver, ParentType, ContextType>;
@@ -4851,7 +4851,7 @@ export type RecommendationResolvers;
note?: Resolver, ParentType, ContextType>;
recommendedAt?: Resolver;
- user?: Resolver;
+ user?: Resolver, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn;
};
@@ -5016,7 +5016,7 @@ export type SearchItemResolvers, ParentType, ContextType>;
readingProgressAnchorIndex?: Resolver;
readingProgressPercent?: Resolver;
- recommendedBy?: Resolver>, ParentType, ContextType>;
+ recommendations?: Resolver>, ParentType, ContextType>;
savedAt?: Resolver;
shortId?: Resolver, ParentType, ContextType>;
siteIcon?: Resolver, ParentType, ContextType>;
diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql
index 7178aa080..89f6cbf6b 100644
--- a/packages/api/src/generated/schema.graphql
+++ b/packages/api/src/generated/schema.graphql
@@ -86,7 +86,7 @@ type Article {
readAt: Date
readingProgressAnchorIndex: Int!
readingProgressPercent: Float!
- recommendedBy: [Recommendation!]
+ recommendations: [Recommendation!]
savedAt: Date!
savedByViewer: Boolean
shareInfo: LinkShareInfo
@@ -1265,7 +1265,7 @@ type Recommendation {
name: String!
note: String
recommendedAt: Date!
- user: RecommendingUser!
+ user: RecommendingUser
}
type RecommendationGroup {
@@ -1498,7 +1498,7 @@ type SearchItem {
readAt: Date
readingProgressAnchorIndex: Int!
readingProgressPercent: Float!
- recommendedBy: [Recommendation!]
+ recommendations: [Recommendation!]
savedAt: Date!
shortId: String
siteIcon: String
diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts
index 695e47664..6333aba8a 100644
--- a/packages/api/src/resolvers/article/index.ts
+++ b/packages/api/src/resolvers/article/index.ts
@@ -1015,6 +1015,7 @@ export const updatesSinceResolver = authorized<
}
})
+ console.log('edges', JSON.stringify(edges))
return {
edges,
pageInfo: {
diff --git a/packages/api/src/resolvers/recommendations/index.ts b/packages/api/src/resolvers/recommendations/index.ts
index d86d45bde..d3e90df89 100644
--- a/packages/api/src/resolvers/recommendations/index.ts
+++ b/packages/api/src/resolvers/recommendations/index.ts
@@ -185,7 +185,8 @@ export const recommendResolver = authorized<
member.user.id,
page.id,
{
- ...group,
+ id: group.id,
+ name: group.name,
note: input.note ?? null,
user: {
userId: user.id,
diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts
index a281a70de..de1a182e3 100755
--- a/packages/api/src/schema.ts
+++ b/packages/api/src/schema.ts
@@ -384,7 +384,7 @@ const schema = gql`
state: ArticleSavingRequestStatus
language: String
readAt: Date
- recommendedBy: [Recommendation!]
+ recommendations: [Recommendation!]
}
# Query: article
@@ -1531,7 +1531,7 @@ const schema = gql`
savedAt: Date!
highlights: [Highlight!]
siteIcon: String
- recommendedBy: [Recommendation!]
+ recommendations: [Recommendation!]
}
type SearchItemEdge {
diff --git a/packages/db/elastic_migrations/index_settings.json b/packages/db/elastic_migrations/index_settings.json
index ec53bce1a..cebf4c58b 100644
--- a/packages/db/elastic_migrations/index_settings.json
+++ b/packages/db/elastic_migrations/index_settings.json
@@ -167,7 +167,7 @@
"wordsCount": {
"type": "integer"
},
- "recommendedBy": {
+ "recommendations": {
"type": "nested",
"properties": {
"id": {
diff --git a/packages/db/migrate.ts b/packages/db/migrate.ts
index 01b6ef065..767ac5efc 100755
--- a/packages/db/migrate.ts
+++ b/packages/db/migrate.ts
@@ -5,6 +5,7 @@ import chalk from 'chalk'
import { Client } from '@elastic/elasticsearch'
import { readFileSync } from 'fs'
import { join } from 'path'
+import { print } from 'graphql'
dotenv.config()
@@ -109,6 +110,86 @@ const createIndex = async (): Promise => {
})
}
+const reIndex = async (): Promise => {
+ const newPages = INDEX_NAME + '-002'
+ // // create index
+ // try {
+ // await esClient.indices.delete({
+ // index: newPages,
+ // })
+ // } catch (e) {
+ // console.log('001: ', e)
+ // }
+
+ // try {
+ // await esClient.indices.delete({
+ // index: 'pages-temp',
+ // })
+ // } catch (e) {
+ // console.log('002: ', e)
+ // }
+
+ // try {
+ // await esClient.indices.delete({
+ // index: 'pages-temp-001',
+ // })
+ // } catch (e) {
+ // console.log('003: ', e)
+ // }
+
+ // try {
+ // await esClient.indices.delete({
+ // index: 'pages-temp',
+ // })
+ // } catch (e) {
+ // console.log('002: ', e)
+ // }
+
+ try {
+ log('getting pages alias:')
+ const result = await esClient.indices.getAlias({
+ index: 'pages_alias',
+ })
+ log('result ' + JSON.stringify(result.body))
+ } catch (err) {
+ log('fetch error: ' + err)
+ }
+
+ log('creating newPages')
+ await esClient.indices.create({
+ index: newPages,
+ body: JSON.parse(indexSettings),
+ })
+
+ log('reindexing')
+ await esClient.reindex({
+ body: {
+ source: { index: INDEX_NAME },
+ dest: { index: newPages },
+ },
+ wait_for_completion: true,
+ })
+
+ console.log('updateAliases')
+
+ await esClient.indices.updateAliases({
+ body: {
+ actions: [
+ {
+ add: { index: newPages, alias: 'pages_alias', is_write_index: true },
+ },
+ ],
+ },
+ })
+
+ console.log('updateAliases 002')
+ esClient.indices.updateAliases({
+ body: {
+ actions: [{ remove: { index: INDEX_NAME, alias: 'pages_alias' } }],
+ },
+ })
+}
+
const updateMappings = async (): Promise => {
// update mappings
await esClient.indices.putMapping({
@@ -131,59 +212,85 @@ const postgresMigration = postgrator
process.exit(1)
})
+// console.log('reindexing')
+// reIndex()
+// .then(() => console.log('reindexed elastic'))
+// .catch((err) => console.log('error reindexing', err))
+
// elastic migration
log('Creating elastic index...')
-const elasticMigration = esClient.indices
- .exists({ index: INDEX_ALIAS })
- .then(({ body: exists }) => {
- if (!exists) {
- return createIndex().then(() => log('Elastic index created.'))
- } else {
- log('Elastic index already exists.')
- }
- })
- .then(() => {
- log('Updating elastic index mappings...')
- return updateMappings().then(() => {
- log('Elastic index mappings updated.')
+const indexTest = async (): Promise => {
+ log('running indexTest')
+ try {
+ const result = await esClient.indices.getAlias({
+ index: 'pages_alias',
})
- })
- .then(() => {
- log('Adding default state to pages in elastic...')
- return esClient
- .update_by_query({
- index: INDEX_ALIAS,
- requests_per_second: 250,
- scroll_size: 500,
- timeout: '30m',
- body: {
- script: {
- source: 'ctx._source.state = params.state',
- lang: 'painless',
- params: {
- state: 'SUCCEEDED',
- },
- },
- query: {
- bool: {
- must_not: [
- {
- exists: {
- field: 'state',
- },
- },
- ],
- },
- },
- },
- })
- .then(() => log('Default state added.'))
- })
- .catch((error) => {
- log(`${chalk.red('Elastic migration failed: ')}${error.message}`, chalk.red)
- const { appliedMigrations } = error
- logAppliedMigrations(appliedMigrations)
- process.exit(1)
- })
+ log('result ' + JSON.stringify(result.body))
+ } catch (err) {
+ log('fetch error: ' + err)
+ }
+}
-Promise.all([postgresMigration, elasticMigration]).then(() => log('Exiting...'))
+reIndex().then(() => console.log('ran reindex'))
+// indexTest().then(() => console.log('ran index test'))
+
+// const elasticMigration = esClient.indices
+// .exists({ index: INDEX_ALIAS })
+// .then(({ body: exists }) => {
+// if (!exists) {
+// return createIndex().then(() => log('Elastic index created.'))
+// } else {
+// log('Elastic index already exists.')
+// }
+// })
+// .then(() => {
+// log('Updating elastic index mappings...')
+// return updateMappings().then(() => {
+// log('Elastic index mappings updated.')
+// })
+// })
+// .then(() => {
+// log('Adding default state to pages in elastic...')
+// return esClient
+// .update_by_query({
+// index: INDEX_ALIAS,
+// requests_per_second: 250,
+// scroll_size: 500,
+// timeout: '30m',
+// body: {
+// script: {
+// source: 'ctx._source.state = params.state',
+// lang: 'painless',
+// params: {
+// state: 'SUCCEEDED',
+// },
+// },
+// query: {
+// bool: {
+// must_not: [
+// {
+// exists: {
+// field: 'state',
+// },
+// },
+// ],
+// },
+// },
+// },
+// })
+// .then(() => log('Default state added.'))
+// })
+// .catch((error) => {
+// console.log('error', JSON.stringify(error))
+// log(`${chalk.red('Elastic migration failed: ')}${error.message}`, chalk.red)
+// const { appliedMigrations } = error
+// logAppliedMigrations(appliedMigrations)
+// process.exit(1)
+// })
+
+Promise.all([
+ // postgresMigration,
+ // indexTest,
+ // reIndex,
+ /* elasticMigration */
+]).then(() => log('Exiting...'))
diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
index 287ee1a43..be7893b41 100644
--- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
@@ -82,7 +82,7 @@ export type LibraryItemNode = {
siteName?: string
subscription?: string
readAt?: string
- recommendedBy?: Recommendation[]
+ recommendations?: Recommendation[]
}
export type Recommendation = {
@@ -151,7 +151,7 @@ export function useGetLibraryItemsQuery({
siteName
subscription
readAt
- recommendedBy {
+ recommendations {
id
name
note