feat: Implement /api/v2/instance to conditionally show Translation option
This commit is contained in:
parent
58dcadf642
commit
2d9e980215
|
@ -27,6 +27,7 @@ extension StatusSection {
|
|||
static let logger = Logger(subsystem: "StatusSection", category: "logic")
|
||||
|
||||
struct Configuration {
|
||||
let context: AppContext
|
||||
let authContext: AuthContext
|
||||
weak var statusTableViewCellDelegate: StatusTableViewCellDelegate?
|
||||
weak var timelineMiddleLoaderTableViewCellDelegate: TimelineMiddleLoaderTableViewCellDelegate?
|
||||
|
@ -250,6 +251,7 @@ extension StatusSection {
|
|||
statusView: cell.statusView
|
||||
)
|
||||
|
||||
cell.statusView.viewModel.context = configuration.context
|
||||
cell.statusView.viewModel.authContext = configuration.authContext
|
||||
|
||||
cell.configure(
|
||||
|
|
|
@ -18,6 +18,7 @@ extension DiscoveryCommunityViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
|
|
|
@ -18,6 +18,7 @@ extension DiscoveryPostsViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
|
|
|
@ -20,6 +20,7 @@ extension HashtagTimelineViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
|
|
|
@ -22,6 +22,7 @@ extension HomeTimelineViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: timelineMiddleLoaderTableViewCellDelegate,
|
||||
|
|
|
@ -17,6 +17,7 @@ extension BookmarkViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
|
|
|
@ -17,6 +17,7 @@ extension FavoriteViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
|
|
|
@ -18,6 +18,7 @@ extension UserTimelineViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
|
|
|
@ -24,6 +24,7 @@ extension ThreadViewModel {
|
|||
tableView: tableView,
|
||||
context: context,
|
||||
configuration: StatusSection.Configuration(
|
||||
context: context,
|
||||
authContext: authContext,
|
||||
statusTableViewCellDelegate: statusTableViewCellDelegate,
|
||||
timelineMiddleLoaderTableViewCellDelegate: nil,
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>CoreData 5.xcdatamodel</string>
|
||||
<string>CoreData 6.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21512" systemVersion="22A400" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Application" representedClassName="CoreDataStack.Application" syncable="YES">
|
||||
<attribute name="identifier" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
<attribute name="vapidKey" optional="YES" attributeType="String"/>
|
||||
<attribute name="website" optional="YES" attributeType="String"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="application" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="DomainBlock" representedClassName="CoreDataStack.DomainBlock" syncable="YES">
|
||||
<attribute name="blockedDomain" attributeType="String"/>
|
||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="userID"/>
|
||||
<constraint value="domain"/>
|
||||
<constraint value="blockedDomain"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="Emoji" representedClassName="CoreDataStack.Emoji" syncable="YES">
|
||||
<attribute name="category" optional="YES" attributeType="String"/>
|
||||
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="shortcode" attributeType="String"/>
|
||||
<attribute name="staticURL" attributeType="String"/>
|
||||
<attribute name="url" attributeType="String"/>
|
||||
<attribute name="visibleInPicker" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
</entity>
|
||||
<entity name="Feed" representedClassName="CoreDataStack.Feed" syncable="YES">
|
||||
<attribute name="acctRaw" optional="YES" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="hasMore" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="isLoadingMore" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="kindRaw" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="notification" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Notification" inverseName="feeds" inverseEntity="Notification"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="feeds" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="Instance" representedClassName="CoreDataStack.Instance" syncable="YES">
|
||||
<attribute name="configurationRaw" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="configurationV2Raw" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="version" optional="YES" attributeType="String"/>
|
||||
<relationship name="authentications" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonAuthentication" inverseName="instance" inverseEntity="MastodonAuthentication"/>
|
||||
</entity>
|
||||
<entity name="MastodonAuthentication" representedClassName="CoreDataStack.MastodonAuthentication" syncable="YES">
|
||||
<attribute name="activedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="appAccessToken" attributeType="String"/>
|
||||
<attribute name="clientID" attributeType="String"/>
|
||||
<attribute name="clientSecret" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userAccessToken" attributeType="String"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
<relationship name="instance" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Instance" inverseName="authentications" inverseEntity="Instance"/>
|
||||
<relationship name="user" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="mastodonAuthentication" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="MastodonUser" representedClassName="CoreDataStack.MastodonUser" syncable="YES">
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" attributeType="String"/>
|
||||
<attribute name="avatarStatic" optional="YES" attributeType="String"/>
|
||||
<attribute name="bot" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="displayName" attributeType="String"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="emojis" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="fields" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="followersCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="followingCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="header" attributeType="String"/>
|
||||
<attribute name="headerStatic" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="note" optional="YES" attributeType="String"/>
|
||||
<attribute name="statusesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="suspended" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="url" optional="YES" attributeType="String"/>
|
||||
<attribute name="username" attributeType="String"/>
|
||||
<relationship name="blocking" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="blockingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="blockingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="blocking" inverseEntity="MastodonUser"/>
|
||||
<relationship name="bookmarked" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="bookmarkedBy" inverseEntity="Status"/>
|
||||
<relationship name="domainBlocking" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="domainBlockingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="domainBlockingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="domainBlocking" inverseEntity="MastodonUser"/>
|
||||
<relationship name="endorsed" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="endorsedBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="endorsedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="endorsed" inverseEntity="MastodonUser"/>
|
||||
<relationship name="favourite" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="favouritedBy" inverseEntity="Status"/>
|
||||
<relationship name="followedTags" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Tag" inverseName="followedBy" inverseEntity="Tag"/>
|
||||
<relationship name="following" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="followingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="followingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="following" inverseEntity="MastodonUser"/>
|
||||
<relationship name="followRequested" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="followRequestedBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="followRequestedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="followRequested" inverseEntity="MastodonUser"/>
|
||||
<relationship name="mastodonAuthentication" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonAuthentication" inverseName="user" inverseEntity="MastodonAuthentication"/>
|
||||
<relationship name="muted" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="mutedBy" inverseEntity="Status"/>
|
||||
<relationship name="muting" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="mutingBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="mutingBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muting" inverseEntity="MastodonUser"/>
|
||||
<relationship name="notifications" toMany="YES" deletionRule="Nullify" destinationEntity="Notification" inverseName="account" inverseEntity="Notification"/>
|
||||
<relationship name="pinnedStatus" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="pinnedBy" inverseEntity="Status"/>
|
||||
<relationship name="privateNotes" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="to" inverseEntity="PrivateNote"/>
|
||||
<relationship name="privateNotesTo" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PrivateNote" inverseName="from" inverseEntity="PrivateNote"/>
|
||||
<relationship name="reblogged" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="rebloggedBy" inverseEntity="Status"/>
|
||||
<relationship name="searchHistories" toMany="YES" deletionRule="Nullify" destinationEntity="SearchHistory" inverseName="account" inverseEntity="SearchHistory"/>
|
||||
<relationship name="showingReblogs" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="showingReblogsBy" inverseEntity="MastodonUser"/>
|
||||
<relationship name="showingReblogsBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="showingReblogs" inverseEntity="MastodonUser"/>
|
||||
<relationship name="statuses" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="author" inverseEntity="Status"/>
|
||||
<relationship name="votePollOptions" toMany="YES" deletionRule="Nullify" destinationEntity="PollOption" inverseName="votedBy" inverseEntity="PollOption"/>
|
||||
<relationship name="votePolls" toMany="YES" deletionRule="Nullify" destinationEntity="Poll" inverseName="votedBy" inverseEntity="Poll"/>
|
||||
</entity>
|
||||
<entity name="Notification" representedClassName="CoreDataStack.Notification" syncable="YES">
|
||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="followRequestState" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="transientFollowRequestState" optional="YES" transient="YES" attributeType="Binary"/>
|
||||
<attribute name="typeRaw" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<relationship name="account" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="notifications" inverseEntity="MastodonUser"/>
|
||||
<relationship name="feeds" toMany="YES" deletionRule="Cascade" destinationEntity="Feed" inverseName="notification" inverseEntity="Feed"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="notifications" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="Poll" representedClassName="CoreDataStack.Poll" syncable="YES">
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="expired" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="expiresAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="isVoting" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="multiple" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="votersCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="votesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="options" toMany="YES" deletionRule="Cascade" destinationEntity="PollOption" inverseName="poll" inverseEntity="PollOption"/>
|
||||
<relationship name="status" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="poll" inverseEntity="Status"/>
|
||||
<relationship name="votedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="votePolls" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="PollOption" representedClassName="CoreDataStack.PollOption" syncable="YES">
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="index" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isSelected" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="title" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="votesCount" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="poll" maxCount="1" deletionRule="Nullify" destinationEntity="Poll" inverseName="options" inverseEntity="Poll"/>
|
||||
<relationship name="votedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="votePollOptions" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="PrivateNote" representedClassName="CoreDataStack.PrivateNote" syncable="YES">
|
||||
<attribute name="note" optional="YES" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="from" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="privateNotesTo" inverseEntity="MastodonUser"/>
|
||||
<relationship name="to" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="privateNotes" inverseEntity="MastodonUser"/>
|
||||
</entity>
|
||||
<entity name="SearchHistory" representedClassName="CoreDataStack.SearchHistory" syncable="YES">
|
||||
<attribute name="createAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userID" attributeType="String" defaultValueString=""/>
|
||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="searchHistories" inverseEntity="MastodonUser"/>
|
||||
<relationship name="hashtag" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Tag" inverseName="searchHistories" inverseEntity="Tag"/>
|
||||
<relationship name="status" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="searchHistories" inverseEntity="Status"/>
|
||||
</entity>
|
||||
<entity name="Setting" representedClassName="CoreDataStack.Setting" syncable="YES">
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="preferredStaticAvatar" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="preferredStaticEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="preferredTrueBlackDarkMode" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="preferredUsingDefaultBrowser" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userID" attributeType="String"/>
|
||||
<relationship name="subscriptions" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Subscription" inverseName="setting" inverseEntity="Subscription"/>
|
||||
</entity>
|
||||
<entity name="Status" representedClassName="CoreDataStack.Status" syncable="YES">
|
||||
<attribute name="attachments" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="content" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="deletedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String"/>
|
||||
<attribute name="emojis" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="favouritesCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="String"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="inReplyToAccountID" optional="YES" attributeType="String"/>
|
||||
<attribute name="inReplyToID" optional="YES" attributeType="String"/>
|
||||
<attribute name="isSensitiveToggled" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="language" optional="YES" attributeType="String"/>
|
||||
<attribute name="mentions" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="reblogsCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="repliesCount" optional="YES" attributeType="Integer 64" usesScalarValueType="NO"/>
|
||||
<attribute name="revealedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="sensitive" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="spoilerText" optional="YES" attributeType="String"/>
|
||||
<attribute name="text" optional="YES" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uri" attributeType="String"/>
|
||||
<attribute name="url" optional="YES" attributeType="String"/>
|
||||
<attribute name="visibilityRaw" optional="YES" attributeType="String" elementID="visibility"/>
|
||||
<relationship name="application" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Application" inverseName="status" inverseEntity="Application"/>
|
||||
<relationship name="author" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="statuses" inverseEntity="MastodonUser"/>
|
||||
<relationship name="bookmarkedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="bookmarked" inverseEntity="MastodonUser"/>
|
||||
<relationship name="favouritedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="favourite" inverseEntity="MastodonUser"/>
|
||||
<relationship name="feeds" toMany="YES" deletionRule="Cascade" destinationEntity="Feed" inverseName="status" inverseEntity="Feed"/>
|
||||
<relationship name="mutedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="muted" inverseEntity="MastodonUser"/>
|
||||
<relationship name="notifications" toMany="YES" deletionRule="Cascade" destinationEntity="Notification" inverseName="status" inverseEntity="Notification"/>
|
||||
<relationship name="pinnedBy" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="pinnedStatus" inverseEntity="MastodonUser"/>
|
||||
<relationship name="poll" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Poll" inverseName="status" inverseEntity="Poll"/>
|
||||
<relationship name="reblog" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="reblogFrom" inverseEntity="Status"/>
|
||||
<relationship name="reblogFrom" toMany="YES" deletionRule="Cascade" destinationEntity="Status" inverseName="reblog" inverseEntity="Status"/>
|
||||
<relationship name="rebloggedBy" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="reblogged" inverseEntity="MastodonUser"/>
|
||||
<relationship name="replyFrom" toMany="YES" deletionRule="Nullify" destinationEntity="Status" inverseName="replyTo" inverseEntity="Status"/>
|
||||
<relationship name="replyTo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Status" inverseName="replyFrom" inverseEntity="Status"/>
|
||||
<relationship name="searchHistories" toMany="YES" deletionRule="Cascade" destinationEntity="SearchHistory" inverseName="status" inverseEntity="SearchHistory"/>
|
||||
</entity>
|
||||
<entity name="Subscription" representedClassName="CoreDataStack.Subscription" syncable="YES">
|
||||
<attribute name="activedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="endpoint" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" optional="YES" attributeType="String"/>
|
||||
<attribute name="policyRaw" attributeType="String"/>
|
||||
<attribute name="serverKey" optional="YES" attributeType="String"/>
|
||||
<attribute name="updatedAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="userToken" optional="YES" attributeType="String"/>
|
||||
<relationship name="alert" maxCount="1" deletionRule="Cascade" destinationEntity="SubscriptionAlerts" inverseName="subscription" inverseEntity="SubscriptionAlerts"/>
|
||||
<relationship name="setting" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Setting" inverseName="subscriptions" inverseEntity="Setting"/>
|
||||
</entity>
|
||||
<entity name="SubscriptionAlerts" representedClassName="CoreDataStack.SubscriptionAlerts" syncable="YES">
|
||||
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="favouriteRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="followRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="followRequestRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mentionRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="pollRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="reblogRaw" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="subscription" maxCount="1" deletionRule="Nullify" destinationEntity="Subscription" inverseName="alert" inverseEntity="Subscription"/>
|
||||
</entity>
|
||||
<entity name="Tag" representedClassName="CoreDataStack.Tag" syncable="YES">
|
||||
<attribute name="createAt" attributeType="Date" defaultDateTimeInterval="631123200" usesScalarValueType="NO"/>
|
||||
<attribute name="domain" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="following" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="histories" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="identifier" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
<attribute name="updatedAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="url" attributeType="String"/>
|
||||
<relationship name="followedBy" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="MastodonUser" inverseName="followedTags" inverseEntity="MastodonUser"/>
|
||||
<relationship name="searchHistories" toMany="YES" deletionRule="Nullify" destinationEntity="SearchHistory" inverseName="hashtag" inverseEntity="SearchHistory"/>
|
||||
</entity>
|
||||
</model>
|
|
@ -16,7 +16,8 @@ public final class Instance: NSManagedObject {
|
|||
@NSManaged public private(set) var updatedAt: Date
|
||||
|
||||
@NSManaged public private(set) var configurationRaw: Data?
|
||||
|
||||
@NSManaged public private(set) var configurationV2Raw: Data?
|
||||
|
||||
// MARK: one-to-many relationships
|
||||
@NSManaged public var authentications: Set<MastodonAuthentication>
|
||||
}
|
||||
|
@ -44,6 +45,10 @@ extension Instance {
|
|||
self.configurationRaw = configurationRaw
|
||||
}
|
||||
|
||||
public func update(configurationV2Raw: Data?) {
|
||||
self.configurationV2Raw = configurationV2Raw
|
||||
}
|
||||
|
||||
public func didUpdate(at networkDate: Date) {
|
||||
self.updatedAt = networkDate
|
||||
}
|
||||
|
|
|
@ -25,8 +25,42 @@ extension Instance {
|
|||
}
|
||||
|
||||
extension Instance {
|
||||
public var canFollowTags: Bool {
|
||||
guard let majorVersionString = version?.split(separator: ".").first else { return false }
|
||||
return Int(majorVersionString) == 4 // following Tags is support beginning with Mastodon v4.0.0
|
||||
public var configurationV2: Mastodon.Entity.V2.Instance.Configuration? {
|
||||
guard let configurationRaw = configurationV2Raw else { return nil }
|
||||
guard let configuration = try? JSONDecoder().decode(Mastodon.Entity.V2.Instance.Configuration.self, from: configurationRaw) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
static func encodeV2(configuration: Mastodon.Entity.V2.Instance.Configuration) -> Data? {
|
||||
return try? JSONEncoder().encode(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
extension Instance {
|
||||
public var canFollowTags: Bool {
|
||||
version?.majorServerVersion(greaterThanOrEquals: 4) ?? false // following Tags is support beginning with Mastodon v4.0.0
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
public func majorServerVersion(greaterThanOrEquals comparedVersion: Int) -> Bool {
|
||||
guard
|
||||
let majorVersionString = split(separator: ".").first,
|
||||
let majorVersionInt = Int(majorVersionString)
|
||||
else { return false }
|
||||
|
||||
return majorVersionInt >= comparedVersion
|
||||
}
|
||||
}
|
||||
|
||||
extension Instance {
|
||||
var isTranslationEnabled: Bool {
|
||||
if let configuration = configurationV2 {
|
||||
return configuration.translation?.enabled == true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,9 @@ extension APIService {
|
|||
return Mastodon.API.Instance.instance(session: session, domain: domain)
|
||||
}
|
||||
|
||||
public func instanceV2(
|
||||
domain: String
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.V2.Instance>, Error> {
|
||||
return Mastodon.API.V2.Instance.instance(session: session, domain: domain)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import os.log
|
||||
import Foundation
|
||||
import CoreData
|
||||
import CoreDataStack
|
||||
import MastodonSDK
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
static func createOrMergeInstanceV2(
|
||||
into managedObjectContext: NSManagedObjectContext,
|
||||
domain: String,
|
||||
entity: Mastodon.Entity.V2.Instance,
|
||||
networkDate: Date,
|
||||
log: Logger
|
||||
) -> (instance: Instance, isCreated: Bool) {
|
||||
// fetch old mastodon user
|
||||
let old: Instance? = {
|
||||
let request = Instance.sortedFetchRequest
|
||||
request.predicate = Instance.predicate(domain: domain)
|
||||
request.fetchLimit = 1
|
||||
request.returnsObjectsAsFaults = false
|
||||
do {
|
||||
return try managedObjectContext.fetch(request).first
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
|
||||
if let old = old {
|
||||
APIService.CoreData.mergeV2(
|
||||
instance: old,
|
||||
entity: entity,
|
||||
domain: domain,
|
||||
networkDate: networkDate
|
||||
)
|
||||
return (old, false)
|
||||
} else {
|
||||
let instance = Instance.insert(
|
||||
into: managedObjectContext,
|
||||
property: Instance.Property(domain: domain, version: entity.version)
|
||||
)
|
||||
let configurationRaw = entity.configuration.flatMap { Instance.encodeV2(configuration: $0) }
|
||||
instance.update(configurationV2Raw: configurationRaw)
|
||||
|
||||
return (instance, true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension APIService.CoreData {
|
||||
|
||||
static func mergeV2(
|
||||
instance: Instance,
|
||||
entity: Mastodon.Entity.V2.Instance,
|
||||
domain: String,
|
||||
networkDate: Date
|
||||
) {
|
||||
guard networkDate > instance.updatedAt else { return }
|
||||
|
||||
let configurationRaw = entity.configuration.flatMap { Instance.encodeV2(configuration: $0) }
|
||||
instance.update(configurationV2Raw: configurationRaw)
|
||||
instance.version = entity.version
|
||||
|
||||
instance.didUpdate(at: networkDate)
|
||||
}
|
||||
|
||||
}
|
|
@ -50,42 +50,18 @@ extension InstanceService {
|
|||
func updateInstance(domain: String) {
|
||||
guard let apiService = self.apiService else { return }
|
||||
apiService.instance(domain: domain)
|
||||
.flatMap { response -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Instance>, Error> in
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
// get instance
|
||||
let (instance, _) = APIService.CoreData.createOrMergeInstance(
|
||||
into: managedObjectContext,
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
networkDate: response.networkDate,
|
||||
log: Logger(subsystem: "Update", category: "InstanceService")
|
||||
)
|
||||
|
||||
// update relationship
|
||||
let request = MastodonAuthentication.sortedFetchRequest
|
||||
request.predicate = MastodonAuthentication.predicate(domain: domain)
|
||||
request.returnsObjectsAsFaults = false
|
||||
do {
|
||||
let authentications = try managedObjectContext.fetch(request)
|
||||
for authentication in authentications {
|
||||
authentication.update(instance: instance)
|
||||
}
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
}
|
||||
.flatMap { [unowned self] response -> AnyPublisher<Void, Error> in
|
||||
if response.value.version?.majorServerVersion(greaterThanOrEquals: 4) == true {
|
||||
return apiService.instanceV2(domain: domain)
|
||||
.flatMap { return self.updateInstanceV2(domain: domain, response: $0) }
|
||||
.eraseToAnyPublisher()
|
||||
} else {
|
||||
return self.updateInstance(domain: domain, response: response)
|
||||
}
|
||||
.setFailureType(to: Error.self)
|
||||
.tryMap { result -> Mastodon.Response.Content<Mastodon.Entity.Instance> in
|
||||
switch result {
|
||||
case .success:
|
||||
return response
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
// .flatMap { [unowned self] response -> AnyPublisher<Void, Error> in
|
||||
// return
|
||||
// }
|
||||
.sink { [weak self] completion in
|
||||
guard let self = self else { return }
|
||||
switch completion {
|
||||
|
@ -100,6 +76,80 @@ extension InstanceService {
|
|||
}
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
private func updateInstance(domain: String, response: Mastodon.Response.Content<Mastodon.Entity.Instance>) -> AnyPublisher<Void, Error> {
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
// get instance
|
||||
let (instance, _) = APIService.CoreData.createOrMergeInstance(
|
||||
into: managedObjectContext,
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
networkDate: response.networkDate,
|
||||
log: Logger(subsystem: "Update", category: "InstanceService")
|
||||
)
|
||||
|
||||
// update relationship
|
||||
let request = MastodonAuthentication.sortedFetchRequest
|
||||
request.predicate = MastodonAuthentication.predicate(domain: domain)
|
||||
request.returnsObjectsAsFaults = false
|
||||
do {
|
||||
let authentications = try managedObjectContext.fetch(request)
|
||||
for authentication in authentications {
|
||||
authentication.update(instance: instance)
|
||||
}
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
.setFailureType(to: Error.self)
|
||||
.tryMap { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
private func updateInstanceV2(domain: String, response: Mastodon.Response.Content<Mastodon.Entity.V2.Instance>) -> AnyPublisher<Void, Error> {
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
return managedObjectContext.performChanges {
|
||||
// get instance
|
||||
let (instance, _) = APIService.CoreData.createOrMergeInstanceV2(
|
||||
into: managedObjectContext,
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
networkDate: response.networkDate,
|
||||
log: Logger(subsystem: "Update", category: "InstanceService")
|
||||
)
|
||||
|
||||
// update relationship
|
||||
let request = MastodonAuthentication.sortedFetchRequest
|
||||
request.predicate = MastodonAuthentication.predicate(domain: domain)
|
||||
request.returnsObjectsAsFaults = false
|
||||
do {
|
||||
let authentications = try managedObjectContext.fetch(request)
|
||||
for authentication in authentications {
|
||||
authentication.update(instance: instance)
|
||||
}
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
.setFailureType(to: Error.self)
|
||||
.tryMap { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
public extension InstanceService {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
|
||||
extension Mastodon.API.V2.Instance {
|
||||
|
||||
private static func instanceEndpointURL(domain: String) -> URL {
|
||||
return Mastodon.API.endpointV2URL(domain: domain).appendingPathComponent("instance")
|
||||
}
|
||||
|
||||
/// Information about the server
|
||||
///
|
||||
/// - Since: 4.0.0
|
||||
/// - Version: 4.0.0
|
||||
/// # Last Update
|
||||
/// 2022/12/09
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/methods/instance/)
|
||||
/// - Parameters:
|
||||
/// - session: `URLSession`
|
||||
/// - domain: Mastodon instance domain. e.g. "example.com"
|
||||
/// - Returns: `AnyPublisher` contains `Instance` nested in the response
|
||||
public static func instance(
|
||||
session: URLSession,
|
||||
domain: String
|
||||
) -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.V2.Instance>, Error> {
|
||||
let request = Mastodon.API.get(
|
||||
url: instanceEndpointURL(domain: domain),
|
||||
query: nil,
|
||||
authorization: nil
|
||||
)
|
||||
return session.dataTaskPublisher(for: request)
|
||||
.tryMap { data, response in
|
||||
let value: Mastodon.Entity.V2.Instance
|
||||
|
||||
do {
|
||||
value = try Mastodon.API.decode(type: Mastodon.Entity.V2.Instance.self, from: data, response: response)
|
||||
} catch {
|
||||
if let response = response as? HTTPURLResponse, 400 ..< 500 ~= response.statusCode {
|
||||
// For example, AUTHORIZED_FETCH may result in authentication errors
|
||||
value = Mastodon.Entity.V2.Instance(domain: domain)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
return Mastodon.Response.Content(value: value, response: response)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
}
|
|
@ -126,6 +126,7 @@ extension Mastodon.API.V2 {
|
|||
public enum Search { }
|
||||
public enum Suggestions { }
|
||||
public enum Media { }
|
||||
public enum Instance { }
|
||||
}
|
||||
|
||||
extension Mastodon.API {
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
import Foundation
|
||||
|
||||
extension Mastodon.Entity.V2 {
|
||||
/// Instance
|
||||
///
|
||||
/// - Since: 4.0.0
|
||||
/// - Version: 4.0.3
|
||||
/// # Last Update
|
||||
/// 2022/12/09
|
||||
/// # Reference
|
||||
/// [Document](https://docs.joinmastodon.org/entities/instance/)
|
||||
public struct Instance: Codable {
|
||||
|
||||
public let domain: String?
|
||||
public let title: String
|
||||
public let description: String
|
||||
public let shortDescription: String?
|
||||
public let email: String?
|
||||
public let version: String?
|
||||
public let languages: [String]? // (ISO 639 Part 1-5 language codes)
|
||||
public let registrations: Mastodon.Entity.V2.Instance.Registrations?
|
||||
public let approvalRequired: Bool?
|
||||
public let invitesEnabled: Bool?
|
||||
public let urls: Mastodon.Entity.Instance.InstanceURL?
|
||||
public let statistics: Mastodon.Entity.Instance.Statistics?
|
||||
|
||||
public let thumbnail: Thumbnail?
|
||||
public let contactAccount: Mastodon.Entity.Account?
|
||||
public let rules: [Mastodon.Entity.Instance.Rule]?
|
||||
|
||||
// https://github.com/mastodon/mastodon/pull/16485
|
||||
public let configuration: Configuration?
|
||||
|
||||
public init(domain: String, approvalRequired: Bool? = nil) {
|
||||
self.domain = domain
|
||||
self.title = domain
|
||||
self.description = ""
|
||||
self.shortDescription = nil
|
||||
self.email = ""
|
||||
self.version = nil
|
||||
self.languages = nil
|
||||
self.registrations = nil
|
||||
self.approvalRequired = approvalRequired
|
||||
self.invitesEnabled = nil
|
||||
self.urls = nil
|
||||
self.statistics = nil
|
||||
self.thumbnail = nil
|
||||
self.contactAccount = nil
|
||||
self.rules = nil
|
||||
self.configuration = nil
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case domain
|
||||
case title
|
||||
case description
|
||||
case shortDescription = "short_description"
|
||||
case email
|
||||
case version
|
||||
case languages
|
||||
case registrations
|
||||
case approvalRequired = "approval_required"
|
||||
case invitesEnabled = "invites_enabled"
|
||||
case urls
|
||||
case statistics = "stats"
|
||||
|
||||
case thumbnail
|
||||
case contactAccount = "contact_account"
|
||||
case rules
|
||||
|
||||
case configuration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.V2.Instance {
|
||||
public struct Configuration: Codable {
|
||||
public let statuses: Mastodon.Entity.Instance.Configuration.Statuses?
|
||||
public let mediaAttachments: Mastodon.Entity.Instance.Configuration.MediaAttachments?
|
||||
public let polls: Mastodon.Entity.Instance.Configuration.Polls?
|
||||
public let translation: Mastodon.Entity.V2.Instance.Configuration.Translation?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case statuses
|
||||
case mediaAttachments = "media_attachments"
|
||||
case polls
|
||||
case translation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.V2.Instance {
|
||||
public struct Registrations: Codable {
|
||||
public let enabled: Bool
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.V2.Instance.Configuration {
|
||||
public struct Translation: Codable {
|
||||
public let enabled: Bool
|
||||
}
|
||||
}
|
||||
|
||||
extension Mastodon.Entity.V2.Instance {
|
||||
public struct Thumbnail: Codable {
|
||||
public let url: String?
|
||||
}
|
||||
}
|
||||
|
||||
//extension Mastodon.Entity.V2.Instance {
|
||||
// public struct Statuses: Codable {
|
||||
// public let maxCharacters: Int
|
||||
// public let maxMediaAttachments: Int
|
||||
// public let charactersReservedPerURL: Int
|
||||
//
|
||||
// enum CodingKeys: String, CodingKey {
|
||||
// case maxCharacters = "max_characters"
|
||||
// case maxMediaAttachments = "max_media_attachments"
|
||||
// case charactersReservedPerURL = "characters_reserved_per_url"
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public struct MediaAttachments: Codable {
|
||||
// public let supportedMIMETypes: [String]
|
||||
// public let imageSizeLimit: Int
|
||||
// public let imageMatrixLimit: Int
|
||||
// public let videoSizeLimit: Int
|
||||
// public let videoFrameRateLimit: Int
|
||||
// public let videoMatrixLimit: Int
|
||||
//
|
||||
// enum CodingKeys: String, CodingKey {
|
||||
// case supportedMIMETypes = "supported_mime_types"
|
||||
// case imageSizeLimit = "image_size_limit"
|
||||
// case imageMatrixLimit = "image_matrix_limit"
|
||||
// case videoSizeLimit = "video_size_limit"
|
||||
// case videoFrameRateLimit = "video_frame_rate_limit"
|
||||
// case videoMatrixLimit = "video_matrix_limit"
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public struct Polls: Codable {
|
||||
// public let maxOptions: Int
|
||||
// public let maxCharactersPerOption: Int
|
||||
// public let minExpiration: Int
|
||||
// public let maxExpiration: Int
|
||||
//
|
||||
// enum CodingKeys: String, CodingKey {
|
||||
// case maxOptions = "max_options"
|
||||
// case maxCharactersPerOption = "max_characters_per_option"
|
||||
// case minExpiration = "min_expiration"
|
||||
// case maxExpiration = "max_expiration"
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public struct Translation: Codable {
|
||||
// public let enabled: Bool
|
||||
// }
|
||||
//}
|
|
@ -23,7 +23,8 @@ extension NotificationView {
|
|||
public var objects = Set<NSManagedObject>()
|
||||
|
||||
let logger = Logger(subsystem: "NotificationView", category: "ViewModel")
|
||||
|
||||
|
||||
@Published public var context: AppContext?
|
||||
@Published public var authContext: AuthContext?
|
||||
|
||||
@Published public var type: MastodonNotificationType?
|
||||
|
@ -57,6 +58,9 @@ extension NotificationView.ViewModel {
|
|||
bindAuthorMenu(notificationView: notificationView)
|
||||
bindFollowRequest(notificationView: notificationView)
|
||||
|
||||
$context
|
||||
.assign(to: \.context, on: notificationView.statusView.viewModel)
|
||||
.store(in: &disposeBag)
|
||||
$authContext
|
||||
.assign(to: \.authContext, on: notificationView.statusView.viewModel)
|
||||
.store(in: &disposeBag)
|
||||
|
@ -209,7 +213,7 @@ extension NotificationView.ViewModel {
|
|||
$isTranslated
|
||||
)
|
||||
)
|
||||
.sink { authorName, isMuting, isBlocking, isMyselfIsTranslated in
|
||||
.sink { [weak self] authorName, isMuting, isBlocking, isMyselfIsTranslated in
|
||||
guard let name = authorName?.string else {
|
||||
notificationView.menuButton.menu = nil
|
||||
return
|
||||
|
@ -217,12 +221,29 @@ extension NotificationView.ViewModel {
|
|||
|
||||
let (isMyself, isTranslated) = isMyselfIsTranslated
|
||||
|
||||
lazy var instanceConfigurationV2: Mastodon.Entity.V2.Instance.Configuration? = {
|
||||
guard
|
||||
let self = self,
|
||||
let context = self.context,
|
||||
let authContext = self.authContext
|
||||
else { return nil }
|
||||
|
||||
var configuration: Mastodon.Entity.V2.Instance.Configuration? = nil
|
||||
context.managedObjectContext.performAndWait {
|
||||
guard let authentication = authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)
|
||||
else { return }
|
||||
configuration = authentication.instance?.configurationV2
|
||||
}
|
||||
return configuration
|
||||
}()
|
||||
|
||||
let menuContext = NotificationView.AuthorMenuContext(
|
||||
name: name,
|
||||
isMuting: isMuting,
|
||||
isBlocking: isBlocking,
|
||||
isMyself: isMyself,
|
||||
isBookmarking: false, // no bookmark action display for notification item
|
||||
isTranslationEnabled: instanceConfigurationV2?.translation?.enabled == true,
|
||||
isTranslated: isTranslated,
|
||||
statusLanguage: ""
|
||||
)
|
||||
|
|
|
@ -150,6 +150,7 @@ extension StatusAuthorView {
|
|||
public let isMyself: Bool
|
||||
public let isBookmarking: Bool
|
||||
|
||||
public let isTranslationEnabled: Bool
|
||||
public let isTranslated: Bool
|
||||
public let statusLanguage: String?
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ extension StatusAuthorView {
|
|||
var actions = [MastodonMenu.Action]()
|
||||
|
||||
if !menuContext.isMyself {
|
||||
if let statusLanguage = menuContext.statusLanguage, !menuContext.isTranslated {
|
||||
if let statusLanguage = menuContext.statusLanguage, menuContext.isTranslationEnabled, !menuContext.isTranslated {
|
||||
actions.append(
|
||||
.translateStatus(.init(language: statusLanguage))
|
||||
)
|
||||
|
|
|
@ -27,6 +27,7 @@ extension StatusView {
|
|||
|
||||
let logger = Logger(subsystem: "StatusView", category: "ViewModel")
|
||||
|
||||
public var context: AppContext?
|
||||
public var authContext: AuthContext?
|
||||
public var originalStatus: Status?
|
||||
|
||||
|
@ -609,12 +610,28 @@ extension StatusView.ViewModel {
|
|||
return
|
||||
}
|
||||
|
||||
lazy var instanceConfigurationV2: Mastodon.Entity.V2.Instance.Configuration? = {
|
||||
guard
|
||||
let context = self.context,
|
||||
let authContext = self.authContext
|
||||
else { return nil }
|
||||
|
||||
var configuration: Mastodon.Entity.V2.Instance.Configuration? = nil
|
||||
context.managedObjectContext.performAndWait {
|
||||
guard let authentication = authContext.mastodonAuthenticationBox.authenticationRecord.object(in: context.managedObjectContext)
|
||||
else { return }
|
||||
configuration = authentication.instance?.configurationV2
|
||||
}
|
||||
return configuration
|
||||
}()
|
||||
|
||||
let menuContext = StatusAuthorView.AuthorMenuContext(
|
||||
name: name,
|
||||
isMuting: isMuting,
|
||||
isBlocking: isBlocking,
|
||||
isMyself: isMyself,
|
||||
isBookmarking: isBookmark,
|
||||
isTranslationEnabled: instanceConfigurationV2?.translation?.enabled == true,
|
||||
isTranslated: translatedFromLanguage != nil,
|
||||
statusLanguage: language
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue