mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 08:10:59 +00:00
Adjust schema to accomodate moved account property
This commit is contained in:
parent
4b0f896f64
commit
81169ffd2f
10 changed files with 258 additions and 37 deletions
17
DB/Sources/DB/Content/AccountResult.swift
Normal file
17
DB/Sources/DB/Content/AccountResult.swift
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import Mastodon
|
||||
|
||||
struct AccountResult: Codable, Hashable, FetchableRecord {
|
||||
let account: StoredAccount
|
||||
let moved: StoredAccount?
|
||||
}
|
||||
|
||||
extension QueryInterfaceRequest where RowDecoder == StoredAccount {
|
||||
var accountResultRequest: AnyFetchRequest<AccountResult> {
|
||||
AnyFetchRequest(including(optional: StoredAccount.moved))
|
||||
.asRequest(of: AccountResult.self)
|
||||
}
|
||||
}
|
|
@ -193,7 +193,7 @@ private extension ContentDatabase {
|
|||
var migrator = DatabaseMigrator()
|
||||
|
||||
migrator.registerMigration("createStatuses") { db in
|
||||
try db.create(table: "account", ifNotExists: true) { t in
|
||||
try db.create(table: "storedAccount", ifNotExists: true) { t in
|
||||
t.column("id", .text).notNull().primaryKey(onConflict: .replace)
|
||||
t.column("username", .text).notNull()
|
||||
t.column("acct", .text).notNull()
|
||||
|
@ -213,13 +213,14 @@ private extension ContentDatabase {
|
|||
t.column("emojis", .blob).notNull()
|
||||
t.column("bot", .boolean).notNull()
|
||||
t.column("discoverable", .boolean)
|
||||
t.column("movedId", .text).indexed().references("storedAccount", column: "id")
|
||||
}
|
||||
|
||||
try db.create(table: "storedStatus", ifNotExists: true) { t in
|
||||
t.column("id", .text).notNull().primaryKey(onConflict: .replace)
|
||||
t.column("uri", .text).notNull()
|
||||
t.column("createdAt", .datetime).notNull()
|
||||
t.column("accountId", .text).indexed().notNull().references("account", column: "id")
|
||||
t.column("accountId", .text).indexed().notNull().references("storedAccount", column: "id")
|
||||
t.column("content", .text).notNull()
|
||||
t.column("visibility", .text).notNull()
|
||||
t.column("sensitive", .boolean).notNull()
|
||||
|
|
|
@ -5,17 +5,33 @@ import GRDB
|
|||
import Mastodon
|
||||
|
||||
struct StatusResult: Codable, Hashable, FetchableRecord {
|
||||
let account: Account
|
||||
let account: StoredAccount
|
||||
let accountMoved: StoredAccount?
|
||||
let status: StoredStatus
|
||||
let reblogAccount: Account?
|
||||
let reblogAccount: StoredAccount?
|
||||
let reblogAccountMoved: StoredAccount?
|
||||
let reblog: StoredStatus?
|
||||
}
|
||||
|
||||
extension StatusResult {
|
||||
var accountResult: AccountResult {
|
||||
AccountResult(account: account, moved: accountMoved)
|
||||
}
|
||||
|
||||
var reblogAccountResult: AccountResult? {
|
||||
guard let reblogAccount = reblogAccount else { return nil }
|
||||
|
||||
return AccountResult(account: reblogAccount, moved: reblogAccountMoved)
|
||||
}
|
||||
}
|
||||
|
||||
extension QueryInterfaceRequest where RowDecoder == StoredStatus {
|
||||
var statusResultRequest: QueryInterfaceRequest<StatusResult> {
|
||||
including(required: StoredStatus.account)
|
||||
var statusResultRequest: AnyFetchRequest<StatusResult> {
|
||||
AnyFetchRequest(including(required: StoredStatus.account)
|
||||
.including(optional: StoredStatus.accountMoved)
|
||||
.including(optional: StoredStatus.reblogAccount)
|
||||
.including(optional: StoredStatus.reblog)
|
||||
.including(optional: StoredStatus.reblogAccountMoved)
|
||||
.including(optional: StoredStatus.reblog))
|
||||
.asRequest(of: StatusResult.self)
|
||||
}
|
||||
}
|
||||
|
|
65
DB/Sources/DB/Content/StoredAccount.swift
Normal file
65
DB/Sources/DB/Content/StoredAccount.swift
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import Mastodon
|
||||
|
||||
struct StoredAccount: Codable, Hashable {
|
||||
let id: String
|
||||
let username: String
|
||||
let acct: String
|
||||
let displayName: String
|
||||
let locked: Bool
|
||||
let createdAt: Date
|
||||
let followersCount: Int
|
||||
let followingCount: Int
|
||||
let statusesCount: Int
|
||||
let note: HTML
|
||||
let url: URL
|
||||
let avatar: URL
|
||||
let avatarStatic: URL
|
||||
let header: URL
|
||||
let headerStatic: URL
|
||||
let fields: [Account.Field]
|
||||
let emojis: [Emoji]
|
||||
let bot: Bool
|
||||
let discoverable: Bool
|
||||
let movedId: String?
|
||||
}
|
||||
|
||||
extension StoredAccount: FetchableRecord, PersistableRecord {
|
||||
static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||
MastodonDecoder()
|
||||
}
|
||||
|
||||
static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||
MastodonEncoder()
|
||||
}
|
||||
}
|
||||
|
||||
extension StoredAccount {
|
||||
static let moved = belongsTo(StoredAccount.self, key: "moved")
|
||||
|
||||
init(account: Account) {
|
||||
id = account.id
|
||||
username = account.username
|
||||
acct = account.acct
|
||||
displayName = account.displayName
|
||||
locked = account.locked
|
||||
createdAt = account.createdAt
|
||||
followersCount = account.followersCount
|
||||
followingCount = account.followingCount
|
||||
statusesCount = account.statusesCount
|
||||
note = account.note
|
||||
url = account.url
|
||||
avatar = account.avatar
|
||||
avatarStatic = account.avatarStatic
|
||||
header = account.header
|
||||
headerStatic = account.headerStatic
|
||||
fields = account.fields
|
||||
emojis = account.emojis
|
||||
bot = account.bot
|
||||
discoverable = account.discoverable
|
||||
movedId = account.moved?.id
|
||||
}
|
||||
}
|
|
@ -47,8 +47,19 @@ extension StoredStatus: FetchableRecord, PersistableRecord {
|
|||
}
|
||||
|
||||
extension StoredStatus {
|
||||
static let account = belongsTo(Account.self, key: "account")
|
||||
static let reblogAccount = hasOne(Account.self, through: Self.reblog, using: Self.account, key: "reblogAccount")
|
||||
static let account = belongsTo(StoredAccount.self, key: "account", using: ForeignKey([Column("accountId")]))
|
||||
static let accountMoved = hasOne(StoredAccount.self,
|
||||
through: Self.account,
|
||||
using: StoredAccount.moved,
|
||||
key: "accountMoved")
|
||||
static let reblogAccount = hasOne(StoredAccount.self,
|
||||
through: Self.reblog,
|
||||
using: Self.account,
|
||||
key: "reblogAccount")
|
||||
static let reblogAccountMoved = hasOne(StoredAccount.self,
|
||||
through: Self.reblogAccount,
|
||||
using: StoredAccount.moved,
|
||||
key: "reblogAccountMoved")
|
||||
static let reblog = belongsTo(StoredStatus.self, key: "reblog")
|
||||
static let ancestorJoins = hasMany(StatusContextJoin.self, using: ForeignKey([Column("parentID")]))
|
||||
.filter(Column("section") == StatusContextJoin.Section.ancestors.rawValue)
|
||||
|
@ -63,23 +74,11 @@ extension StoredStatus {
|
|||
through: descendantJoins,
|
||||
using: StatusContextJoin.status)
|
||||
|
||||
var account: QueryInterfaceRequest<Account> {
|
||||
request(for: Self.account)
|
||||
}
|
||||
|
||||
var reblogAccount: QueryInterfaceRequest<Account> {
|
||||
request(for: Self.reblogAccount)
|
||||
}
|
||||
|
||||
var reblog: QueryInterfaceRequest<StoredStatus> {
|
||||
request(for: Self.reblog)
|
||||
}
|
||||
|
||||
var ancestors: QueryInterfaceRequest<StatusResult> {
|
||||
var ancestors: AnyFetchRequest<StatusResult> {
|
||||
request(for: Self.ancestors).statusResultRequest
|
||||
}
|
||||
|
||||
var descendants: QueryInterfaceRequest<StatusResult> {
|
||||
var descendants: AnyFetchRequest<StatusResult> {
|
||||
request(for: Self.descendants).statusResultRequest
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,45 @@ import Foundation
|
|||
import GRDB
|
||||
import Mastodon
|
||||
|
||||
extension Account: FetchableRecord, PersistableRecord {
|
||||
public static func databaseJSONDecoder(for column: String) -> JSONDecoder {
|
||||
MastodonDecoder()
|
||||
extension Account {
|
||||
func save(_ db: Database) throws {
|
||||
if let moved = moved {
|
||||
try StoredAccount(account: moved).save(db)
|
||||
}
|
||||
|
||||
public static func databaseJSONEncoder(for column: String) -> JSONEncoder {
|
||||
MastodonEncoder()
|
||||
try StoredAccount(account: self).save(db)
|
||||
}
|
||||
|
||||
convenience init(accountResult: AccountResult) {
|
||||
var moved: Account?
|
||||
|
||||
if let movedResult = accountResult.moved {
|
||||
moved = Self(storedAccount: movedResult, moved: nil)
|
||||
}
|
||||
|
||||
self.init(storedAccount: accountResult.account, moved: moved)
|
||||
}
|
||||
|
||||
convenience init(storedAccount: StoredAccount, moved: Account?) {
|
||||
self.init(id: storedAccount.id,
|
||||
username: storedAccount.username,
|
||||
acct: storedAccount.acct,
|
||||
displayName: storedAccount.displayName,
|
||||
locked: storedAccount.locked,
|
||||
createdAt: storedAccount.createdAt,
|
||||
followersCount: storedAccount.followersCount,
|
||||
followingCount: storedAccount.followingCount,
|
||||
statusesCount: storedAccount.statusesCount,
|
||||
note: storedAccount.note,
|
||||
url: storedAccount.url,
|
||||
avatar: storedAccount.avatar,
|
||||
avatarStatic: storedAccount.avatarStatic,
|
||||
header: storedAccount.header,
|
||||
headerStatic: storedAccount.headerStatic,
|
||||
fields: storedAccount.fields,
|
||||
emojis: storedAccount.emojis,
|
||||
bot: storedAccount.bot,
|
||||
discoverable: storedAccount.discoverable,
|
||||
moved: moved)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,11 @@ extension Status {
|
|||
convenience init(statusResult: StatusResult) {
|
||||
var reblog: Status?
|
||||
|
||||
if let reblogResult = statusResult.reblog, let reblogAccount = statusResult.reblogAccount {
|
||||
reblog = Status(storedStatus: reblogResult, account: reblogAccount, reblog: nil)
|
||||
if let reblogResult = statusResult.reblog, let reblogAccount = statusResult.reblogAccountResult {
|
||||
reblog = Status(storedStatus: reblogResult, account: Account(accountResult: reblogAccount), reblog: nil)
|
||||
}
|
||||
|
||||
self.init(storedStatus: statusResult.status, account: statusResult.account, reblog: reblog)
|
||||
self.init(storedStatus: statusResult.status, account: Account(accountResult: statusResult.accountResult), reblog: reblog)
|
||||
}
|
||||
|
||||
convenience init(storedStatus: StoredStatus, account: Account, reblog: Status?) {
|
||||
|
|
|
@ -41,7 +41,7 @@ extension Timeline {
|
|||
using: TimelineStatusJoin.status)
|
||||
.order(Column("createdAt").desc)
|
||||
|
||||
var statuses: QueryInterfaceRequest<StatusResult> {
|
||||
var statuses: AnyFetchRequest<StatusResult> {
|
||||
request(for: Self.statuses).statusResultRequest
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public struct Account: Codable, Hashable {
|
||||
public final class Account: Codable, Identifiable {
|
||||
public struct Field: Codable, Hashable {
|
||||
public let name: String
|
||||
public let value: HTML
|
||||
|
@ -28,4 +28,95 @@ public struct Account: Codable, Hashable {
|
|||
public let emojis: [Emoji]
|
||||
@DecodableDefault.False public private(set) var bot: Bool
|
||||
@DecodableDefault.False public private(set) var discoverable: Bool
|
||||
public var moved: Account?
|
||||
|
||||
public init(id: String,
|
||||
username: String,
|
||||
acct: String,
|
||||
displayName: String,
|
||||
locked: Bool,
|
||||
createdAt: Date,
|
||||
followersCount: Int,
|
||||
followingCount: Int,
|
||||
statusesCount: Int,
|
||||
note: HTML,
|
||||
url: URL,
|
||||
avatar: URL,
|
||||
avatarStatic: URL,
|
||||
header: URL,
|
||||
headerStatic: URL,
|
||||
fields: [Account.Field],
|
||||
emojis: [Emoji],
|
||||
bot: Bool,
|
||||
discoverable: Bool,
|
||||
moved: Account?) {
|
||||
self.id = id
|
||||
self.username = username
|
||||
self.acct = acct
|
||||
self.displayName = displayName
|
||||
self.locked = locked
|
||||
self.createdAt = createdAt
|
||||
self.followersCount = followersCount
|
||||
self.followingCount = followingCount
|
||||
self.statusesCount = statusesCount
|
||||
self.note = note
|
||||
self.url = url
|
||||
self.avatar = avatar
|
||||
self.avatarStatic = avatarStatic
|
||||
self.header = header
|
||||
self.headerStatic = headerStatic
|
||||
self.fields = fields
|
||||
self.emojis = emojis
|
||||
self.bot = bot
|
||||
self.discoverable = discoverable
|
||||
self.moved = moved
|
||||
}
|
||||
}
|
||||
|
||||
extension Account: Hashable {
|
||||
public static func == (lhs: Account, rhs: Account) -> Bool {
|
||||
return lhs.id == rhs.id &&
|
||||
lhs.username == rhs.username &&
|
||||
lhs.acct == rhs.acct &&
|
||||
lhs.displayName == rhs.displayName &&
|
||||
lhs.locked == rhs.locked &&
|
||||
lhs.createdAt == rhs.createdAt &&
|
||||
lhs.followersCount == rhs.followersCount &&
|
||||
lhs.followingCount == rhs.followingCount &&
|
||||
lhs.statusesCount == rhs.statusesCount &&
|
||||
lhs.note == rhs.note &&
|
||||
lhs.url == rhs.url &&
|
||||
lhs.avatar == rhs.avatar &&
|
||||
lhs.avatarStatic == rhs.avatarStatic &&
|
||||
lhs.header == rhs.header &&
|
||||
lhs.headerStatic == rhs.headerStatic &&
|
||||
lhs.fields == rhs.fields &&
|
||||
lhs.emojis == rhs.emojis &&
|
||||
lhs._bot == rhs._bot &&
|
||||
lhs._discoverable == rhs._discoverable &&
|
||||
lhs.moved == rhs.moved
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
hasher.combine(username)
|
||||
hasher.combine(acct)
|
||||
hasher.combine(displayName)
|
||||
hasher.combine(locked)
|
||||
hasher.combine(createdAt)
|
||||
hasher.combine(followersCount)
|
||||
hasher.combine(followingCount)
|
||||
hasher.combine(statusesCount)
|
||||
hasher.combine(note)
|
||||
hasher.combine(url)
|
||||
hasher.combine(avatar)
|
||||
hasher.combine(avatarStatic)
|
||||
hasher.combine(header)
|
||||
hasher.combine(headerStatic)
|
||||
hasher.combine(fields)
|
||||
hasher.combine(emojis)
|
||||
hasher.combine(bot)
|
||||
hasher.combine(discoverable)
|
||||
hasher.combine(moved)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ public final class Status: Codable, Identifiable {
|
|||
@DecodableDefault.False public private(set) var bookmarked: Bool
|
||||
public let pinned: Bool?
|
||||
|
||||
// Xcode-generated memberwise initializer
|
||||
public init(
|
||||
id: String,
|
||||
uri: String,
|
||||
|
|
Loading…
Reference in a new issue