Merge pull request #18 from VernissageApp/develop

Merge 1.1.0 into main
This commit is contained in:
Marcin Czachurski 2023-04-07 11:14:48 +02:00 committed by GitHub
commit 6125301b02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 813 additions and 195 deletions

View File

@ -28,6 +28,7 @@ extension ApplicationSettings {
@NSManaged public var showSensitive: Bool
@NSManaged public var showPhotoDescription: Bool
@NSManaged public var menuPosition: Int32
}
extension ApplicationSettings: Identifiable {

View File

@ -107,6 +107,12 @@ class ApplicationSettingsHandler {
CoreDataHandler.shared.save()
}
func set(menuPosition: MenuPosition) {
let defaultSettings = self.get()
defaultSettings.menuPosition = Int32(menuPosition.rawValue)
CoreDataHandler.shared.save()
}
private func createApplicationSettingsEntity(viewContext: NSManagedObjectContext? = nil) -> ApplicationSettings {
let context = viewContext ?? CoreDataHandler.shared.container.viewContext
return ApplicationSettings(context: context)

View File

@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Vernissage-006.xcdatamodel</string>
<string>Vernissage-007.xcdatamodel</string>
</dict>
</plist>

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22E252" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AccountData" representedClassName="AccountData" syncable="YES">
<attribute name="accessToken" optional="YES" attributeType="String"/>
<attribute name="acct" attributeType="String"/>
<attribute name="avatar" optional="YES" attributeType="URI"/>
<attribute name="avatarData" optional="YES" attributeType="Binary"/>
<attribute name="clientId" attributeType="String"/>
<attribute name="clientSecret" attributeType="String"/>
<attribute name="clientVapidKey" attributeType="String"/>
<attribute name="createdAt" attributeType="String"/>
<attribute name="displayName" optional="YES" attributeType="String"/>
<attribute name="followersCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="followingCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="header" optional="YES" attributeType="URI"/>
<attribute name="id" attributeType="String"/>
<attribute name="lastSeenStatusId" optional="YES" attributeType="String"/>
<attribute name="locked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="note" optional="YES" attributeType="String"/>
<attribute name="refreshToken" optional="YES" attributeType="String"/>
<attribute name="serverUrl" attributeType="URI"/>
<attribute name="statusesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="url" optional="YES" attributeType="URI"/>
<attribute name="username" attributeType="String"/>
<relationship name="statuses" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="StatusData" inverseName="pixelfedAccount" inverseEntity="StatusData"/>
</entity>
<entity name="ApplicationSettings" representedClassName="ApplicationSettings" syncable="YES">
<attribute name="activeIcon" attributeType="String" defaultValueString="Default"/>
<attribute name="avatarShape" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
<attribute name="currentAccount" optional="YES" attributeType="String"/>
<attribute name="hapticAnimationEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="hapticButtonPressEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="hapticNotificationEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="hapticRefreshEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="hapticTabSelectionEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="lastRefreshTokens" attributeType="Date" defaultDateTimeInterval="694256400" usesScalarValueType="NO"/>
<attribute name="menuPosition" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="YES"/>
<attribute name="showPhotoDescription" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="showSensitive" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="theme" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="tintColor" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="YES"/>
</entity>
<entity name="AttachmentData" representedClassName="AttachmentData" syncable="YES">
<attribute name="blurhash" optional="YES" attributeType="String"/>
<attribute name="data" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES"/>
<attribute name="exifCamera" optional="YES" attributeType="String"/>
<attribute name="exifCreatedDate" optional="YES" attributeType="String"/>
<attribute name="exifExposure" optional="YES" attributeType="String"/>
<attribute name="exifLens" optional="YES" attributeType="String"/>
<attribute name="id" attributeType="String"/>
<attribute name="metaImageHeight" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="metaImageWidth" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="previewUrl" optional="YES" attributeType="URI"/>
<attribute name="remoteUrl" optional="YES" attributeType="URI"/>
<attribute name="statusId" attributeType="String"/>
<attribute name="text" optional="YES" attributeType="String"/>
<attribute name="type" attributeType="String"/>
<attribute name="url" attributeType="URI"/>
<relationship name="statusRelation" maxCount="1" deletionRule="Nullify" destinationEntity="StatusData" inverseName="attachmentsRelation" inverseEntity="StatusData"/>
</entity>
<entity name="StatusData" representedClassName="StatusData" syncable="YES">
<attribute name="accountAvatar" optional="YES" attributeType="URI"/>
<attribute name="accountDisplayName" optional="YES" attributeType="String"/>
<attribute name="accountId" attributeType="String"/>
<attribute name="accountUsername" optional="YES" attributeType="String"/>
<attribute name="applicationName" optional="YES" attributeType="String"/>
<attribute name="applicationWebsite" optional="YES" attributeType="URI"/>
<attribute name="bookmarked" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="content" attributeType="String"/>
<attribute name="createdAt" attributeType="String"/>
<attribute name="favourited" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="favouritesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="String"/>
<attribute name="inReplyToAccount" optional="YES" attributeType="String"/>
<attribute name="inReplyToId" optional="YES" attributeType="String"/>
<attribute name="muted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="pinned" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="reblogged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="rebloggedAccountAvatar" optional="YES" attributeType="URI"/>
<attribute name="rebloggedAccountDisplayName" optional="YES" attributeType="String"/>
<attribute name="rebloggedAccountId" optional="YES" attributeType="String"/>
<attribute name="rebloggedAccountUsername" optional="YES" attributeType="String"/>
<attribute name="rebloggedStatusId" optional="YES" attributeType="String"/>
<attribute name="reblogsCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="repliesCount" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sensitive" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="spoilerText" optional="YES" attributeType="String"/>
<attribute name="uri" attributeType="String"/>
<attribute name="url" optional="YES" attributeType="URI"/>
<attribute name="visibility" attributeType="String"/>
<relationship name="attachmentsRelation" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="AttachmentData" inverseName="statusRelation" inverseEntity="AttachmentData"/>
<relationship name="pixelfedAccount" maxCount="1" deletionRule="Nullify" destinationEntity="AccountData" inverseName="statuses" inverseEntity="AccountData"/>
</entity>
</model>

View File

@ -2,6 +2,7 @@
"global.title.contentWarning" = "Sensitive content";
"global.title.seePost" = "See post";
"global.title.refresh" = "Refresh";
"global.title.momentsAgo" = "moments ago";
// MARK: Global errors.
"global.error.unexpected" = "Unexpected error.";
@ -202,6 +203,12 @@
"settings.title.privacyPolicy" = "Privacy policy";
"settings.title.terms" = "Terms & Conditions";
"settings.title.sourceCode" = "Source code";
"settings.title.rate" = "Rate Vernissage";
"settings.title.socials" = "Socials";
"settings.title.menuPosition" = "Menu position";
"settings.title.topMenu" = "Navigation bar";
"settings.title.bottomRightMenu" = "Bottom right";
"settings.title.bottomLeftMenu" = "Bottom left";
// Mark: Signin view.
"signin.navigationBar.title" = "Sign in to Pixelfed";
@ -266,10 +273,10 @@
// Mark: In-app purchases.
"purchase.donut.title" = "Donut";
"purchase.donut.description" = "Treat me to a doughnut.";
"purchase.cofee.title" = "Cofee";
"purchase.cofee.description" = "Treat me to a coffe.";
"purchase.cake.title" = "Cofee & cake";
"purchase.cake.description" = "Treat me to a coffe and cake.";
"purchase.coffee.title" = "Coffee";
"purchase.coffee.description" = "Treat me to a coffee.";
"purchase.cake.title" = "Coffee & cake";
"purchase.cake.description" = "Treat me to a coffee and cake.";
// Mark: Edit profile.
"editProfile.navigationBar.title" = "Edit profile";
@ -318,6 +325,6 @@
"report.title.violence" = "Violence or dangerous organisations";
"report.title.copyright" = "Copyright infringement";
"report.title.impersonation" = "Impersonation";
"report.title.scam" = "Bullying of harassment";
"report.title.scam" = "Bullying or harassment";
"report.title.terrorism" = "Terrorism";
"report.error.notReported" = "Error during sending report.";

View File

@ -0,0 +1,330 @@
// MARK: Common strings.
"global.title.contentWarning" = "Eduki hunkigarria";
"global.title.seePost" = "Ikusi bidalketa";
"global.title.refresh" = "Freskatu";
"global.title.momentsAgo" = "moments ago";
// MARK: Global errors.
"global.error.unexpected" = "Espero ez zen errorea.";
"global.error.statusesNotRetrieved" = "Ez dira egoerak eskuratu.";
"global.error.errorDuringDownloadStatuses" = "Errorea zerbitzaritik egoerak eskuratzean.";
"global.error.errorDuringDownloadHashtag" = "Errorea zerbitzaritik traolak eskuratzean.";
"global.error.hashtagNotExists" = "Traola ez da lehendik existitzen.";
"global.error.errorDuringImageDownload" = "Ezin da irudia eskuratu.";
"global.error.canceledImageDownload" = "Irudiaren deskarga bertan behera utzi da.";
"global.error.errorDuringDataLoad" = "Datuen kargak huts egin du.";
"global.error.errorDuringUserRead" = "Ezin izan da erabiltzailearen kontua eskuratu.";
"global.error.badUrlServer" = "Zerbitzariaren URL okerra.";
"global.error.accessTokenNotFound" = "Ez da sarbide-tokena aurkitu.";
"global.error.errorDuringDownloadStatus" = "Errorea zerbitzaritik egoera eskuratzean.";
"global.error.errorDuringPurchaseVerification" = "Erosketaren egiaztaketak huts egin du.";
// MARK: Main view (main navigation bar).
"mainview.tab.homeTimeline" = "Hasiera";
"mainview.tab.localTimeline" = "Lokala";
"mainview.tab.federatedTimeline" = "Federatua";
"mainview.tab.trendingPhotos" = "Argazkiak";
"mainview.tab.trendingTags" = "Traolak";
"mainview.tab.trendingAccounts" = "Kontuak";
"mainview.tab.userProfile" = "Profila";
"mainview.tab.notifications" = "Jakinarazpenak";
"mainview.tab.search" = "Bilatu";
"mainview.tab.trending" = "Bogan";
// MARK: Main view (leading navigation bar).
"mainview.menu.settings" = "Ezarpenak";
// MARK: Main view (error notifications).
"mainview.error.switchAccounts" = "Ezin da kontua aldatu.";
// MARK: Home timeline.
"home.title.allCaughtUp" = "Egunean zaude";
"home.title.noPhotos" = "Argazkirik ez.";
// MARK: Statuses timeline (local/federated/favourite/bookmarks etc.).
"statuses.navigationBar.localTimeline" = "Lokala";
"statuses.navigationBar.federatedTimeline" = "Federatua";
"statuses.navigationBar.favourites" = "Gogokoak";
"statuses.navigationBar.bookmarks" = "Laster-markak";
"statuses.title.noPhotos" = "Argazkirik ez.";
"statuses.title.tagFollowed" = "Traolari jarraitzen diozu.";
"statuses.title.tagUnfollowed" = "Traola jarraitzeari utzi diozu.";
"statuses.error.loadingStatusesFailed" = "Egoerak kargatzeak huts egin du.";
"statuses.error.tagFollowFailed" = "Traolari jarraitzeak huts egin du.";
"statuses.error.tagUnfollowFailed" = "Traolari jarraitzeari uzteak huts egin du.";
// Mark: Search view.
"search.navigationBar.title" = "Bilatu";
"search.title.placeholder" = "Bilatu...";
"search.title.usersWith" = "%@ duten erabiltzaileak";
"search.title.goToUser" = "Joan %@ erabiltzailera";
"search.title.hashtagWith" = "%@ duten traolak";
"search.title.goToHashtag" = "Joan %@ traolara";
// Mark: Trending statuses.
"trendingStatuses.navigationBar.title" = "Argazkiak";
"trendingStatuses.title.daily" = "Egunekoak";
"trendingStatuses.title.monthly" = "Hilabetekoak";
"trendingStatuses.title.yearly" = "Urtekoak";
"trendingStatuses.error.loadingStatusesFailed" = "Egoerak kargatzeak huts egin du.";
"trendingStatuses.title.noPhotos" = "Argazkirik ez.";
// Mark: Trending tags.
"trendingTags.navigationBar.title" = "Traolak";
"trendingTags.title.noTags" = "Traolarik ez.";
"trendingTags.title.amountOfPosts" = "%d bidalketa";
"trendingTags.error.loadingTagsFailed" = "Traolak kargatzeak huts egin du.";
// Mark: Trending accounts.
"trendingAccounts.navigationBar.title" = "Kontuak";
"trendingAccounts.title.noAccounts" = "Inor ez.";
"trendingAccounts.error.loadingAccountsFailed" = "Kontuak kargatzeak huts egin du.";
// Mark: User profile view.
"userProfile.title.openInBrowser" = "Ireki nabigatzailean";
"userProfile.title.share" = "Partekatu";
"userProfile.title.unmute" = "Utzi mututzeari";
"userProfile.title.mute" = "Mututu";
"userProfile.title.unblock" = "Utzi blokeatzeari";
"userProfile.title.block" = "Blokeatu";
"userProfile.title.favourites" = "Gogokoak";
"userProfile.title.bookmarks" = "Laster-markak";
"userProfile.title.posts" = "Bidalketa";
"userProfile.title.followers" = "Jarraitzaile";
"userProfile.title.following" = "Jarraitzen";
"userProfile.title.joined" = "%@ egin zuen bat";
"userProfile.title.unfollow" = "Utzi jarraitzeari";
"userProfile.title.followBack" = "Jarraitu bera ere";
"userProfile.title.follow" = "Jarraitu";
"userProfile.title.instance" = "Instantziari buruzko informazioa";
"userProfile.title.blocks" = "Blokeatutako kontuak";
"userProfile.title.mutes" = "Mutututako kontuak";
"userProfile.title.muted" = "Kontua mututu da";
"userProfile.title.unmuted" = "Kontua mututzeari utzi zaio";
"userProfile.title.blocked" = "Kontua blokeatu da";
"userProfile.title.unblocked" = "Kontua blokeatzeari utzi zaio";
"userProfile.title.report" = "Salatu";
"userProfile.error.notExists" = "Kontua ez da existitzen.";
"userProfile.error.loadingAccountFailed" = "Errorea zerbitzaritik kontua eskuratzean.";
"userProfile.error.muting" = "Mututu/Mututzeari uzteak huts egin du.";
"userProfile.error.block" = "Blokeatu/Blokeatzeari uzteak huts egin du.";
"userProfile.error.relationship" = "Harreman ekintzak huts egin du.";
"userProfile.title.edit" = "Editatu";
// Mark: Notifications view.
"notifications.navigationBar.title" = "Jakinarazpenak";
"notifications.title.noNotifications" = "Ez dago jakinarazpenik.";
"notifications.title.followedYou" = "jarraitu dizu";
"notifications.title.mentionedYou" = "aipatu zaitu";
"notifications.title.boosted" = "bultzatu du";
"notifications.title.favourited" = "gogoko du";
"notifications.title.postedStatus" = "argitaratu du";
"notifications.title.followRequest" = "jarraipen-eskaera bidali dizu";
"notifications.title.poll" = "bozketa";
"notifications.title.updatedStatus" = "egoera eguneratu du";
"notifications.title.signedUp" = "izena eman du";
"notifications.title.newReport" = "txosten berria";
"notifications.error.loadingNotificationsFailed" = "Jakinarazpenak kargatzeak huts egin du.";
// Mark: Compose view.
"compose.navigationBar.title" = "Idatzi";
"compose.title.everyone" = "Edonorentzat ikusgai";
"compose.title.unlisted" = "Zerrendatu gabea";
"compose.title.followers" = "Jarraitzaileentzat ikusgai";
"compose.title.attachPhotoFull" = "Erantsi argazkia eta idatzi buruan duzuna";
"compose.title.attachPhotoMini" = "Idatzi buruan duzuna";
"compose.title.publish" = "Argitaratu";
"compose.title.cancel" = "Utzi";
"compose.title.writeContentWarning" = "Idatzi edukiari buruzko oharra";
"compose.title.commentsWillBeDisabled" = "Iruzkinak ezgaituko dira";
"compose.title.statusPublished" = "Egoera argitaratu da";
"compose.title.tryToUpload" = "Saiatu igotzen";
"compose.title.delete" = "Ezabatu";
"compose.title.edit" = "Editatu";
"compose.error.loadingPhotosFailed" = "Ezin da liburutegiko irudia eskuratu.";
"compose.error.postingPhotoFailed" = "Errorea argazkia argitaratzean.";
"compose.error.postingStatusFailed" = "Errorea egoera argitaratzean.";
// Mark: Photo editor view.
"photoEdit.navigationBar.title" = "Argazkiaren xehetasunak";
"photoEdit.title.photo" = "Argazkia";
"photoEdit.title.accessibility" = "Irisgarritasuna";
"photoEdit.title.accessibilityDescription" = "Ikusmen arazoak dituztenentzat deskribapena";
"photoEdit.title.save" = "Gorde";
"photoEdit.title.cancel" = "Utzi";
"photoEdit.error.updatePhotoFailed" = "Errorea argazkia eguneratzean.";
// Mark: Place selector view.
"placeSelector.navigationBar.title" = "Tokiak";
"placeSelector.title.search" = "Bilatu...";
"placeSelector.title.buttonSearch" = "Bilatu";
"placeSelector.title.cancel" = "Utzi";
"placeSelector.error.loadingPlacesFailed" = "Jakinarazpenak kargatzeak huts egin du.";
// Mark: Settings view.
"settings.navigationBar.title" = "Ezarpenak";
"settings.title.close" = "Itxi";
"settings.title.version" = "Bertsioa";
"settings.title.accounts" = "Kontuak";
"settings.title.newAccount" = "Gehitu kontu";
"settings.title.accent" = "Kolore nagusia";
"settings.title.theme" = "Gaia";
"settings.title.system" = "Sistemak darabilena";
"settings.title.light" = "Argia";
"settings.title.dark" = "Iluna";
"settings.title.avatar" = "Abatarra";
"settings.title.circle" = "Biribila";
"settings.title.rounderRectangle" = "Biribildutako ertzak";
"settings.title.other" = "Beste batzuk";
"settings.title.thirdParty" = "Hirugarrenak";
"settings.title.reportBug" = "Eman errore baten berri";
"settings.title.githubIssues" = "Erroreak Github-en";
"settings.title.follow" = "Jarrai nazazu";
"settings.title.support" = "Eman babesa";
"settings.title.thankYouTitle" = "Eskerrik asko 💕";
"settings.title.thankYouMessage" = "Mila esker erosketagatik. Erosketa handi eta txikiek gure bezeroei kalitatezko produkturik onenak eskaintzeko ametsari eusten laguntzen digute. Espero dugu Vernissage gustuko izatea.";
"settings.title.thankYouClose" = "Itxi";
"settings.title.haptics" = "Hobespen haptikoak";
"settings.title.hapticsTabSelection" = "Fitxak hautatzean";
"settings.title.hapticsButtonPress" = "Botoietan tap egitean";
"settings.title.hapticsListRefresh" = "Zerrendak freskatzean";
"settings.title.hapticsAnimationFinished" = "Animazioak amaitzean";
"settings.title.mediaSettings" = "Multimedia hobespenak";
"settings.title.alwaysShowSensitiveTitle" = "Erakutsi beti NSFW edukia";
"settings.title.alwaysShowSensitiveDescription" = "Erakutsi NSFW (Lanerako Egokia Izan Ez Daitekeen Edukia) gisa markatutako multimedia edukia ohartarazpenik gabe";
"settings.title.alwaysShowAltTitle" = "Erakutsi testu alternatiboa";
"settings.title.alwaysShowAltDescription" = "Erakutsi testu alternatiboa xehetasunen pantailan, baldin badago";
"settings.title.general" = "Orokorra";
"settings.title.applicationIcon" = "Aplikazioaren ikonoa";
"settings.title.followVernissage" = "Jarraitu Vernissage-ri";
"settings.title.mastodonAccount" = "Mastodon kontua";
"settings.title.pixelfedAccount" = "Pixelfed kontua";
"settings.title.openPage" = "Ireki";
"settings.title.privacyPolicy" = "Pribatutasun politika";
"settings.title.terms" = "Erabilera baldintzak";
"settings.title.sourceCode" = "Iturburu kodea";
"settings.title.rate" = "Baloratu Vernissage";
"settings.title.socials" = "Gizarte-sareak";
"settings.title.menuPosition" = "Menuaren kokapena";
"settings.title.topMenu" = "Nabigazio barra";
"settings.title.bottomRightMenu" = "Behe eskumaldean";
"settings.title.bottomLeftMenu" = "Behe ezkerraldean";
// Mark: Signin view.
"signin.navigationBar.title" = "Hasi saioa Pixelfed-en";
"signin.title.serverAddress" = "Zerbitzariaren helbidea";
"signin.title.signIn" = "Hasi saioa";
"signin.title.enterServerAddress" = "Sartu zerbitzariaren helbidea";
"signin.title.howToJoinLink" = "Nola batu Pixelfed-era";
"signin.title.chooseServer" = "Edo aukeratu Pixelfed zerbitzaria";
"signin.title.amountOfUsers" = "%d erabiltzaile";
"signin.title.amountOStatuses" = "%d egoera";
"signin.error.communicationFailed" = "Zerbitzariarekin komunikazioak huts egin du.";
// Mark: Status view.
"status.navigationBar.title" = "Xehetasunak";
"status.title.uploaded" = ">";
"status.title.via" = "%@ bidez";
"status.title.reboostedBy" = "Bultzatu dutenak";
"status.title.favouritedBy" = "Gogoko egin dutenak";
"status.title.openInBrowser" = "Ireki nabigatzailean";
"status.title.shareStatus" = "Partekatu egoera";
"status.title.yourStatus" = "Zure egoera";
"status.title.delete" = "Ezabatu";
"status.title.reboosted" = "Bultzatua";
"status.title.unreboosted" = "Bultzada kendua";
"status.title.favourited" = "Gogoko egina";
"status.title.unfavourited" = "Gogoko egiteari utzia";
"status.title.bookmarked" = "Laster-marka jarria";
"status.title.unbookmarked" = "Laster-marka kendua";
"status.title.statusDeleted" = "Egoera ezabatua";
"status.title.reboost" = "Bultzatu";
"status.title.unreboost" = "Kendu bultzada";
"status.title.favourite" = "Egin gogoko";
"status.title.unfavourite" = "Kendu gogokoa";
"status.title.bookmark" = "Jarri laster-marka";
"status.title.unbookmark" = "Kendu laster-marka";
"status.title.comment" = "Egin iruzkina";
"status.title.report" = "Salatu";
"status.error.loadingStatusFailed" = "Egoera kargatzeak huts egin du.";
"status.error.notFound" = "Egoera ez da dagoeneko existitzen.";
"status.error.loadingCommentsFailed" = "Ezin dira iruzkinak eskuratu.";
"status.error.reboostFailed" = "Bultzadak huts egin du.";
"status.error.favouriteFailed" = "Gogokoak huts egin du.";
"status.error.bookmarkFailed" = "Lastar-markak huts egin du.";
"status.error.deleteFailed" = "Ezabatzeak huts egin du.";
// Mark: Accounts view.
"accounts.navigationBar.followers" = "Jarraitzaile";
"accounts.navigationBar.following" = "Jarraitzen";
"accounts.navigationBar.favouritedBy" = "Honek gogoko egina";
"accounts.navigationBar.reboostedBy" = "Honek bultzatua";
"accounts.navigationBar.blocked" = "Blokeatutako kontuak";
"accounts.navigationBar.mutes" = "Mutututako kontuak";
"accounts.title.noAccounts" = "Inor ez.";
"accounts.error.loadingAccountsFailed" = "Kontuak kargatzeak huts egin du.";
// Mark: Third party view.
"thirdParty.navigationBar.title" = "Hirugarrenak";
// Mark: Widget view.
"widget.title.description" = "Widgeta Pixelfed-eko argazkiekin.";
// Mark: In-app purchases.
"purchase.donut.title" = "Opila";
"purchase.donut.description" = "Eros diezadazu opil bat.";
"purchase.coffee.title" = "Kafea";
"purchase.coffee.description" = "Gonbida nazazu kafe bat hartzera.";
"purchase.cake.title" = "Kafea eta tarta";
"purchase.cake.description" = "Kafea eta tarta erosiko?";
// Mark: Edit profile.
"editProfile.navigationBar.title" = "Editatu profila";
"editProfile.title.displayName" = "Pantaila izena";
"editProfile.title.bio" = "Biografia";
"editProfile.title.website" = "Webgunea";
"editProfile.title.save" = "Gorde";
"editProfile.title.accountSaved" = "Profila eguneratu da.";
"editProfile.title.photoInfo" = "Aldatutako argazkia atzerapen txiki batekin ikusiko da aplikazioan eta web gunean.";
"editProfile.title.privateAccount" = "Babestutako kontua";
"editProfile.title.privateAccountInfo" = "Zure kontua babestuta dagoenean baimendutako pertsonek bakarrik ikus ditzakete zure argazkiak eta bideoak Pixelfed-en. Ez du eraginik izango dagoeneko jarraitzen dizutenengan.";
"editProfile.error.saveAccountFailed" = "Profila gordetzeak huts egin du.";
"editProfile.error.loadingAvatarFailed" = "Abatarra kargatzeak huts egin du.";
"editProfile.error.noProfileData" = "Ezin dira profileko datuak erakutsi.";
"editProfile.error.loadingAccountFailed" = "Errorea zerbitzaritik kontua eskuratzean.";
// Mark: Instance information.
"instance.navigationBar.title" = "Instantzia";
"instance.title.instanceInfo" = "Instantziari buruzko informazioa";
"instance.title.name" = "Izena";
"instance.title.address" = "Helbidea";
"instance.title.email" = "ePosta";
"instance.title.version" = "Bertsioa";
"instance.title.users" = "Erabiltzaileak";
"instance.title.posts" = "Bidalketak";
"instance.title.domains" = "Domeinuak";
"instance.title.registrations" = "Izen emateak";
"instance.title.approvalRequired" = "Onespena behar da";
"instance.title.rules" = "Instantziaren arauak";
"instance.title.contact" = "Harremana";
"instance.title.pixelfedAccount" = "Pixelfed kontua";
"instance.error.noInstanceData" = "Ezin dira instantziaren datuak erakutsi.";
"instance.error.loadingDataFailed" = "Errorea zerbitzaritik instantziaren datuak eskuratzean.";
// Mark: Report screen.
"report.navigationBar.title" = "Salatu";
"report.title.close" = "Itxi";
"report.title.send" = "Bidali";
"report.title.userReported" = "Erabiltzailea salatu da";
"report.title.postReported" = "Bidalketa salatu da";
"report.title.reportType" = "Urraketa mota";
"report.title.spam" = "Spama da";
"report.title.sensitive" = "Biluzia edo sexu-ekintza";
"report.title.abusive" = "Gorroto sustatzen duten hitzaldiak edo ikurrak";
"report.title.underage" = "Adingabea";
"report.title.violence" = "Bortizkeria edo erakunde arriskutsua";
"report.title.copyright" = "Egile-eskubideen urraketa";
"report.title.impersonation" = "Imitatzailea";
"report.title.scam" = "Bullyinga edo jazarpena";
"report.title.terrorism" = "Terrorismoa";
"report.error.notReported" = "Errorea salaketa bidaltzerakoan.";

View File

@ -2,6 +2,7 @@
"global.title.contentWarning" = "Wrażliwe treści";
"global.title.seePost" = "Pokaż zdjęcie";
"global.title.refresh" = "Odśwież";
"global.title.momentsAgo" = "chwilę temu";
// MARK: Global errors.
"global.error.unexpected" = "Wystąpił nieoczekiwany błąd.";
@ -202,6 +203,12 @@
"settings.title.privacyPolicy" = "Polityka prywatności";
"settings.title.terms" = "Zasady i warunki";
"settings.title.sourceCode" = "Kod źródłowy";
"settings.title.rate" = "Oceń Vernissage";
"settings.title.socials" = "Społeczności";
"settings.title.menuPosition" = "Pozycja menu";
"settings.title.topMenu" = "Panel tytułowy";
"settings.title.bottomRightMenu" = "Dolny prawy";
"settings.title.bottomLeftMenu" = "Dolny lewy";
// Mark: Signin view.
"signin.navigationBar.title" = "Zaloguj się do Pixelfed";
@ -266,8 +273,8 @@
// Mark: In-app purchases.
"purchase.donut.title" = "Pączek";
"purchase.donut.description" = "Poczęstuj mnie pączkiem.";
"purchase.cofee.title" = "Kawa";
"purchase.cofee.description" = "Poczęstuj mnie kawą.";
"purchase.coffee.title" = "Kawa";
"purchase.coffee.description" = "Poczęstuj mnie kawą.";
"purchase.cake.title" = "Kawa z ciastkiem";
"purchase.cake.description" = "Poczęstuj mnie kawą i ciastkiem.";

13
Models/MenuPosition.swift Normal file
View File

@ -0,0 +1,13 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//
import Foundation
public enum MenuPosition: Int {
case top = 1
case bottomRight = 2
case bottomLeft = 3
}

View File

@ -0,0 +1,3 @@
// MARK: Network errors.
"global.error.notSuccessResponse" = "Zerbitzariaren erantzuna: %@.";
"global.error.unknownError" = "Espero ez zen errorea.";

View File

@ -146,6 +146,9 @@
F88FAD2B295F43B8009B20C9 /* AccountData+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD29295F43B8009B20C9 /* AccountData+CoreDataProperties.swift */; };
F88FAD2D295F4AD7009B20C9 /* ApplicationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */; };
F88FAD32295F5029009B20C9 /* RemoteFileService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88FAD31295F5029009B20C9 /* RemoteFileService.swift */; };
F8911A1729DE914D00770F44 /* NavigationMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8911A1629DE914D00770F44 /* NavigationMenu.swift */; };
F8911A1A29DEA0F500770F44 /* MenuPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8911A1929DEA0F500770F44 /* MenuPosition.swift */; };
F8911A1B29DEA0F500770F44 /* MenuPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8911A1929DEA0F500770F44 /* MenuPosition.swift */; };
F891E7CE29C35BF50022C449 /* ImageRowItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F891E7CD29C35BF50022C449 /* ImageRowItem.swift */; };
F891E7D029C368750022C449 /* ImageRowItemAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = F891E7CF29C368750022C449 /* ImageRowItemAsync.swift */; };
F897978829681B9C00B22335 /* UserAvatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897978729681B9C00B22335 /* UserAvatar.swift */; };
@ -207,6 +210,7 @@
F8CEEDFA29ABAFD200DBED66 /* ImageFileTranseferable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CEEDF929ABAFD200DBED66 /* ImageFileTranseferable.swift */; };
F8D5444329D4066C002225D6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D5444229D4066C002225D6 /* AppDelegate.swift */; };
F8DF38E429DD68820047F1AA /* ViewOffsetKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF38E329DD68820047F1AA /* ViewOffsetKey.swift */; };
F8DF38E629DDB98A0047F1AA /* SocialsSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF38E529DDB98A0047F1AA /* SocialsSectionView.swift */; };
F8E6D03329CDD52500416CCA /* EditProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E6D03229CDD52500416CCA /* EditProfileView.swift */; };
F8E6D03529CE161B00416CCA /* UIImage+Jpeg.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E6D03429CE161B00416CCA /* UIImage+Jpeg.swift */; };
F8E9391F29C0BCFA002BB3B8 /* ImageContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E9391E29C0BCFA002BB3B8 /* ImageContextMenu.swift */; };
@ -368,6 +372,9 @@
F88FAD29295F43B8009B20C9 /* AccountData+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountData+CoreDataProperties.swift"; sourceTree = "<group>"; };
F88FAD2C295F4AD7009B20C9 /* ApplicationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationState.swift; sourceTree = "<group>"; };
F88FAD31295F5029009B20C9 /* RemoteFileService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteFileService.swift; sourceTree = "<group>"; };
F8911A1629DE914D00770F44 /* NavigationMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationMenu.swift; sourceTree = "<group>"; };
F8911A1829DE9E5500770F44 /* Vernissage-007.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-007.xcdatamodel"; sourceTree = "<group>"; };
F8911A1929DEA0F500770F44 /* MenuPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuPosition.swift; sourceTree = "<group>"; };
F891E7CD29C35BF50022C449 /* ImageRowItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRowItem.swift; sourceTree = "<group>"; };
F891E7CF29C368750022C449 /* ImageRowItemAsync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRowItemAsync.swift; sourceTree = "<group>"; };
F897978729681B9C00B22335 /* UserAvatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAvatar.swift; sourceTree = "<group>"; };
@ -434,6 +441,8 @@
F8CEEDF929ABAFD200DBED66 /* ImageFileTranseferable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFileTranseferable.swift; sourceTree = "<group>"; };
F8D5444229D4066C002225D6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
F8DF38E329DD68820047F1AA /* ViewOffsetKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewOffsetKey.swift; sourceTree = "<group>"; };
F8DF38E529DDB98A0047F1AA /* SocialsSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialsSectionView.swift; sourceTree = "<group>"; };
F8DF38E729DDC3D20047F1AA /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Localizable.strings; sourceTree = "<group>"; };
F8E6D03229CDD52500416CCA /* EditProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileView.swift; sourceTree = "<group>"; };
F8E6D03429CE161B00416CCA /* UIImage+Jpeg.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Jpeg.swift"; sourceTree = "<group>"; };
F8E9391E29C0BCFA002BB3B8 /* ImageContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenu.swift; sourceTree = "<group>"; };
@ -689,6 +698,7 @@
F86167C7297FE781004D1F67 /* AvatarShape.swift */,
F89D6C3E29716E41001DA3D4 /* Theme.swift */,
F87AEB932986C51B00434FB6 /* AppConstants.swift */,
F8911A1929DEA0F500770F44 /* MenuPosition.swift */,
);
path = Models;
sourceTree = "<group>";
@ -713,6 +723,7 @@
F86A4306299AA5E900DF7645 /* ThanksView.swift */,
F8B05ACA29B489B100857221 /* HapticsSectionView.swift */,
F8B05ACD29B48E2F00857221 /* MediaSettingsView.swift */,
F8DF38E529DDB98A0047F1AA /* SocialsSectionView.swift */,
);
path = Subviews;
sourceTree = "<group>";
@ -933,6 +944,7 @@
F8C5E56129892CC300ADF6A7 /* FirstAppear.swift */,
F8CAE63D29B8902D001E0372 /* ClearButton.swift */,
F8E9391E29C0BCFA002BB3B8 /* ImageContextMenu.swift */,
F8911A1629DE914D00770F44 /* NavigationMenu.swift */,
);
path = ViewModifiers;
sourceTree = "<group>";
@ -1050,6 +1062,7 @@
en,
Base,
pl,
eu,
);
mainGroup = F88C245F295C37B80006098B;
packageReferences = (
@ -1135,6 +1148,7 @@
F864F77D29BB9A4600B13921 /* AttachmentData+CoreDataClass.swift in Sources */,
F864F7A629BBA01D00B13921 /* CoreDataError.swift in Sources */,
F864F77E29BB9A4900B13921 /* AttachmentData+CoreDataProperties.swift in Sources */,
F8911A1B29DEA0F500770F44 /* MenuPosition.swift in Sources */,
F864F78229BB9A6500B13921 /* StatusData+CoreDataClass.swift in Sources */,
F864F78329BB9A6800B13921 /* StatusData+CoreDataProperties.swift in Sources */,
F864F78429BB9A6E00B13921 /* ApplicationSettings+CoreDataClass.swift in Sources */,
@ -1182,6 +1196,7 @@
F8864CF129ACFFB80020C534 /* View+Keyboard.swift in Sources */,
F88FAD21295F3944009B20C9 /* HomeFeedView.swift in Sources */,
F8FA991E299FAB92007AB130 /* PhotoEditorView.swift in Sources */,
F8911A1729DE914D00770F44 /* NavigationMenu.swift in Sources */,
F88C2475295C37BB0006098B /* CoreDataHandler.swift in Sources */,
F87AEB972986D16D00434FB6 /* AuthorisationError.swift in Sources */,
F8742FC429990AFB00E9642B /* ClientError.swift in Sources */,
@ -1249,6 +1264,7 @@
F89A46DE296EABA20062125F /* StatusPlaceholderView.swift in Sources */,
F88C2482295C3A4F0006098B /* StatusView.swift in Sources */,
F866F6A329604161002E8F88 /* AccountDataHandler.swift in Sources */,
F8911A1A29DEA0F500770F44 /* MenuPosition.swift in Sources */,
F8996DEB2971D29D0043EEC6 /* View+Transition.swift in Sources */,
F876418B298AC1B80057D362 /* NoDataView.swift in Sources */,
F8E9392129C0DA7E002BB3B8 /* LazyImageState+ImageResponse.swift in Sources */,
@ -1306,6 +1322,7 @@
F8FA9920299FDDC3007AB130 /* TextInputField.swift in Sources */,
F86A4303299A9AF500DF7645 /* TipsStore.swift in Sources */,
F8C5E56229892CC300ADF6A7 /* FirstAppear.swift in Sources */,
F8DF38E629DDB98A0047F1AA /* SocialsSectionView.swift in Sources */,
F864F7A529BBA01D00B13921 /* CoreDataError.swift in Sources */,
F8864CEB29ACBAA80020C534 /* TextModel.swift in Sources */,
F8C14394296AF21B001FE31D /* Double+Round.swift in Sources */,
@ -1339,6 +1356,7 @@
children = (
F835082529BEF9C400DE3247 /* en */,
F835082729BEFA1E00DE3247 /* pl */,
F8DF38E729DDC3D20047F1AA /* eu */,
);
name = Localizable.strings;
sourceTree = "<group>";
@ -1354,7 +1372,7 @@
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 96;
DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1365,7 +1383,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -1382,7 +1400,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 96;
DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1393,7 +1411,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -1530,7 +1548,7 @@
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 96;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;
@ -1547,7 +1565,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1570,7 +1588,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 92;
CURRENT_PROJECT_VERSION = 96;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;
@ -1587,7 +1605,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@ -1724,6 +1742,7 @@
F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
F8911A1829DE9E5500770F44 /* Vernissage-007.xcdatamodel */,
F8EF371429C624DA00669F45 /* Vernissage-006.xcdatamodel */,
F8CAE64129B8F1AF001E0372 /* Vernissage-005.xcdatamodel */,
F8B05ACC29B48DD000857221 /* Vernissage-004.xcdatamodel */,
@ -1732,7 +1751,7 @@
F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */,
F88C2477295C37BB0006098B /* Vernissage.xcdatamodel */,
);
currentVersion = F8EF371429C624DA00669F45 /* Vernissage-006.xcdatamodel */;
currentVersion = F8911A1829DE9E5500770F44 /* Vernissage-007.xcdatamodel */;
path = Vernissage.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;

View File

@ -85,6 +85,9 @@ public class ApplicationState: ObservableObject {
/// Updated user profile.
@Published var updatedProfile: Account?
/// Information which menu should be shown (top or bottom).
@Published var menuPosition = MenuPosition.top
public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?) {
self.account = accountModel
self.lastSeenStatusId = lastSeenStatusId

View File

@ -103,9 +103,15 @@ extension String {
}
func toRelative(_ format: DateFormatType = .isoDate) -> String {
let formatter = RelativeDateTimeFormatter()
let date = self.toDate(format) ?? Date()
guard let date = self.toDate(format) else {
return NSLocalizedString("global.title.momentsAgo", comment: "moments ago")
}
if date.addingTimeInterval(60) > Date.now {
return NSLocalizedString("global.title.momentsAgo", comment: "moments ago")
}
let formatter = RelativeDateTimeFormatter()
return formatter.localizedString(for: date, relativeTo: Date.now)
}
}

View File

@ -30,8 +30,8 @@
"internalID" : "AD04459F",
"localizations" : [
{
"description" : "Treat me to a coffe.",
"displayName" : "Cofee",
"description" : "Treat me to a coffee.",
"displayName" : "Coffee",
"locale" : "en_US"
},
{
@ -41,7 +41,7 @@
}
],
"productID" : "dev.mczachurski.Vernissage.cofee",
"referenceName" : "Cofee",
"referenceName" : "Coffee",
"type" : "Consumable"
},
{
@ -50,8 +50,8 @@
"internalID" : "16E4E30C",
"localizations" : [
{
"description" : "Treat me to a coffe and cake.",
"displayName" : "Cofee & cake",
"description" : "Treat me to a coffee and cake.",
"displayName" : "Coffee & cake",
"locale" : "en_US"
},
{

View File

@ -168,6 +168,16 @@ struct VernissageApp: App {
self.applicationState.activeIcon = defaultSettings.activeIcon
self.applicationState.showSensitive = defaultSettings.showSensitive
self.applicationState.showPhotoDescription = defaultSettings.showPhotoDescription
if let menuPosition = MenuPosition(rawValue: Int(defaultSettings.menuPosition)) {
self.applicationState.menuPosition = menuPosition
}
self.applicationState.hapticTabSelectionEnabled = defaultSettings.hapticTabSelectionEnabled
self.applicationState.hapticRefreshEnabled = defaultSettings.hapticRefreshEnabled
self.applicationState.hapticButtonPressEnabled = defaultSettings.hapticButtonPressEnabled
self.applicationState.hapticAnimationEnabled = defaultSettings.hapticAnimationEnabled
self.applicationState.hapticNotificationEnabled = defaultSettings.hapticNotificationEnabled
}
private func setImagePipelines() {

View File

@ -45,7 +45,7 @@ private struct ImageContextMenu: ViewModifier {
await self.favourite()
}
} label: {
Label("status.title.favourite", systemImage: "hand.thumbsup")
Label("status.title.favourite", systemImage: "star")
}
Button {
@ -83,7 +83,7 @@ private struct ImageContextMenu: ViewModifier {
private func favourite() async {
do {
_ = try await self.client.statuses?.favourite(statusId: self.id)
ToastrService.shared.showSuccess(NSLocalizedString("status.title.favourited", comment: "Favourited"), imageSystemName: "hand.thumbsup.fill")
ToastrService.shared.showSuccess(NSLocalizedString("status.title.favourited", comment: "Favourited"), imageSystemName: "star.fill")
} catch {
ErrorService.shared.handle(error, message: "status.error.favouriteFailed", showToastr: true)
}

View File

@ -0,0 +1,74 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//
import Foundation
import SwiftUI
public extension View {
func navigationMenu<MenuItems>(menuPosition: Binding<MenuPosition>,
@ViewBuilder menuItems: @escaping () -> MenuItems) -> some View where MenuItems: View {
modifier(NavigationMenu(menuPosition: menuPosition, menuItems: menuItems))
}
}
private struct NavigationMenu<MenuItems>: ViewModifier where MenuItems: View {
private let menuItems: () -> MenuItems
@Binding var menuPosition: MenuPosition
init(menuPosition: Binding<MenuPosition>, @ViewBuilder menuItems: @escaping () -> MenuItems) {
self.menuItems = menuItems
self._menuPosition = menuPosition
}
func body(content: Content) -> some View {
if self.menuPosition == .top {
content
} else {
ZStack {
content
VStack(alignment: .trailing) {
Spacer()
HStack {
if self.menuPosition == .bottomRight {
Spacer()
self.menuContent()
.padding(.trailing, 20)
}
if self.menuPosition == .bottomLeft {
self.menuContent()
.padding(.leading, 20)
Spacer()
}
}
}
}
}
}
@ViewBuilder
private func menuContent() -> some View {
Menu {
self.menuItems()
} label: {
Image(systemName: "line.3.horizontal")
.resizable()
.foregroundColor(.mainTextColor.opacity(0.8))
.shadow(radius: 5)
.padding(12)
.frame(width: 44, height: 44)
.background(.ultraThinMaterial)
.clipShape(Circle())
}
}
}

View File

@ -32,21 +32,28 @@ struct MainView: View {
var body: some View {
self.getMainView()
.navigationTitle(navBarTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
self.getLeadingToolbar()
self.getPrincipalToolbar()
self.getTrailingToolbar()
}
.onChange(of: tipsStore.status) { status in
if status == .successful {
withAnimation(.spring()) {
self.routerPath.presentedOverlay = .successPayment
self.tipsStore.reset()
.navigationMenu(menuPosition: $applicationState.menuPosition) {
self.navigationMenuContent()
}
.navigationTitle(navBarTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
self.getLeadingToolbar()
if self.applicationState.menuPosition == .top {
self.getPrincipalToolbar()
}
self.getTrailingToolbar()
}
.onChange(of: tipsStore.status) { status in
if status == .successful {
withAnimation(.spring()) {
self.routerPath.presentedOverlay = .successPayment
self.tipsStore.reset()
}
}
}
}
}
@ViewBuilder
@ -92,97 +99,7 @@ struct MainView: View {
private func getPrincipalToolbar() -> some ToolbarContent {
ToolbarItem(placement: .principal) {
Menu {
Button {
self.switchView(to: .home)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .home))
Image(systemName: "house")
}
}
Button {
self.switchView(to: .local)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .local))
Image(systemName: "building")
}
}
Button {
self.switchView(to: .federated)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .federated))
Image(systemName: "globe.europe.africa")
}
}
Button {
self.switchView(to: .search)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .search))
Image(systemName: "magnifyingglass")
}
}
Divider()
Menu {
Button {
self.switchView(to: .trendingPhotos)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .trendingPhotos))
Image(systemName: "photo.stack")
}
}
Button {
self.switchView(to: .trendingTags)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .trendingTags))
Image(systemName: "tag")
}
}
Button {
self.switchView(to: .trendingAccounts)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .trendingAccounts))
Image(systemName: "person.3")
}
}
} label: {
HStack {
Text("mainview.tab.trending", comment: "Trending menu section")
Image(systemName: "chart.line.uptrend.xyaxis")
}
}
Divider()
Button {
self.switchView(to: .profile)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .profile))
Image(systemName: "person.crop.circle")
}
}
Button {
self.switchView(to: .notifications)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .notifications))
Image(systemName: "bell.badge")
}
}
self.navigationMenuContent()
} label: {
HStack {
Text(navBarTitle, comment: "Navbar title")
@ -244,6 +161,101 @@ struct MainView: View {
}
}
@ViewBuilder
private func navigationMenuContent() -> some View {
Button {
self.switchView(to: .home)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .home))
Image(systemName: "house")
}
}
Button {
self.switchView(to: .local)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .local))
Image(systemName: "building")
}
}
Button {
self.switchView(to: .federated)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .federated))
Image(systemName: "globe.europe.africa")
}
}
Button {
self.switchView(to: .search)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .search))
Image(systemName: "magnifyingglass")
}
}
Divider()
Menu {
Button {
self.switchView(to: .trendingPhotos)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .trendingPhotos))
Image(systemName: "photo.stack")
}
}
Button {
self.switchView(to: .trendingTags)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .trendingTags))
Image(systemName: "tag")
}
}
Button {
self.switchView(to: .trendingAccounts)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .trendingAccounts))
Image(systemName: "person.3")
}
}
} label: {
HStack {
Text("mainview.tab.trending", comment: "Trending menu section")
Image(systemName: "chart.line.uptrend.xyaxis")
}
}
Divider()
Button {
self.switchView(to: .profile)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .profile))
Image(systemName: "person.crop.circle")
}
}
Button {
self.switchView(to: .notifications)
} label: {
HStack {
Text(self.getViewTitle(viewMode: .notifications))
Image(systemName: "bell.badge")
}
}
}
@ViewBuilder
private func getAvatarImage(avatarUrl: URL?, avatarData: Data?) -> some View {
if let avatarData,

View File

@ -192,7 +192,7 @@ struct NotificationRowView: View {
case .reblog:
return Image("custom.rocket")
case .favourite:
return Image(systemName: "hand.thumbsup")
return Image(systemName: "star")
case .status:
return Image(systemName: "photo.on.rectangle.angled")
case .followRequest:

View File

@ -46,6 +46,9 @@ struct SettingsView: View {
// Other.
OtherSectionView()
// Socials.
SocialsSectionView()
// Version.
self.version()
}

View File

@ -29,6 +29,12 @@ struct GeneralSectionView: View {
(Theme.dark, "settings.title.dark")
]
private let menuPositions: [(menuPosition: MenuPosition, name: LocalizedStringKey)] = [
(MenuPosition.top, "settings.title.topMenu"),
(MenuPosition.bottomRight, "settings.title.bottomRightMenu"),
(MenuPosition.bottomLeft, "settings.title.bottomLeftMenu")
]
var body: some View {
Section("settings.title.general") {
@ -64,6 +70,20 @@ struct GeneralSectionView: View {
self.applicationState.theme = theme
ApplicationSettingsHandler.shared.set(theme: theme)
}
// Menu position.
Picker(selection: $applicationState.menuPosition) {
ForEach(self.menuPositions, id: \.menuPosition) { item in
Text(item.name, comment: "Menu positions")
.tag(item.menuPosition)
}
} label: {
Text("settings.title.menuPosition", comment: "Menu position")
}
.onChange(of: self.applicationState.menuPosition) { menuPosition in
self.applicationState.menuPosition = menuPosition
ApplicationSettingsHandler.shared.set(menuPosition: menuPosition)
}
}
}
}

View File

@ -10,75 +10,33 @@ struct OtherSectionView: View {
var body: some View {
Section("settings.title.other") {
NavigationLink(value: RouteurDestinations.thirdParty) {
Text("settings.title.thirdParty", comment: "Third party")
Label("settings.title.thirdParty", systemImage: "shippingbox")
}
HStack {
Text("settings.title.privacyPolicy", comment: "Privacy policy")
Spacer()
Link(NSLocalizedString("settings.title.openPage", comment: "Open"), destination: URL(string: "https://mczachurski.dev/vernissage/privacy-policy.html")!)
.font(.footnote)
Link(destination: URL(string: "https://mczachurski.dev/vernissage/privacy-policy.html")!) {
Label("settings.title.privacyPolicy", systemImage: "hand.raised.square")
}
.tint(.mainTextColor)
HStack {
Text("settings.title.terms", comment: "Terms & Conditions")
Spacer()
Link(NSLocalizedString("settings.title.openPage", comment: "Open"), destination: URL(string: "https://mczachurski.dev/vernissage/terms.html")!)
.font(.footnote)
Link(destination: URL(string: "https://mczachurski.dev/vernissage/terms.html")!) {
Label("settings.title.terms", systemImage: "doc.text")
}
.tint(.mainTextColor)
HStack {
Text("settings.title.sourceCode", comment: "Source code")
Spacer()
Link(NSLocalizedString("settings.title.openPage", comment: "Open"), destination: URL(string: "https://github.com/VernissageApp/Vernissage")!)
.font(.footnote)
Link(destination: URL(string: "https://apps.apple.com/app/id1663543216?action=write-review")!) {
Label("settings.title.rate", systemImage: "star")
}
.tint(.mainTextColor)
HStack {
Text("settings.title.reportBug", comment: "Report a bug")
Spacer()
Link(NSLocalizedString("settings.title.githubIssues", comment: "Issues on GitHub"), destination: URL(string: "https://github.com/VernissageApp/Home/issues")!)
.font(.footnote)
Link(destination: URL(string: "https://github.com/VernissageApp/Vernissage")!) {
Label("settings.title.sourceCode", systemImage: "swift")
}
.tint(.mainTextColor)
HStack {
VStack(alignment: .leading) {
Text("settings.title.followVernissage", comment: "Follow Vernissage")
Text("Mastodon account")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}
Spacer()
Link("@vernissage", destination: URL(string: "https://mastodon.social/@vernissage")!)
.font(.footnote)
}
HStack {
VStack(alignment: .leading) {
Text("settings.title.follow", comment: "Follow me")
Text("settings.title.mastodonAccount", comment: "Mastodon account")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}
Spacer()
Link("@mczachurski", destination: URL(string: "https://mastodon.social/@mczachurski")!)
.font(.footnote)
}
HStack {
VStack(alignment: .leading) {
Text("settings.title.follow", comment: "Follow me")
Text("settings.title.pixelfedAccount", comment: "Pixelfed account")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}
Spacer()
Link("@mczachurski", destination: URL(string: "https://pixelfed.social/@mczachurski")!)
.font(.footnote)
Link(destination: URL(string: "https://github.com/VernissageApp/Vernissage/issues")!) {
Label("settings.title.reportBug", systemImage: "ant")
}
.tint(.mainTextColor)
}
}
}

View File

@ -0,0 +1,52 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//
import SwiftUI
struct SocialsSectionView: View {
var body: some View {
Section("settings.title.socials") {
HStack {
VStack(alignment: .leading) {
Text("settings.title.followVernissage", comment: "Follow Vernissage")
Text("settings.title.mastodonAccount", comment: "Mastodon account")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}
Spacer()
Link("@vernissage", destination: URL(string: "https://mastodon.social/@vernissage")!)
.font(.footnote)
}
HStack {
VStack(alignment: .leading) {
Text("settings.title.follow", comment: "Follow me")
Text("settings.title.mastodonAccount", comment: "Mastodon account")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}
Spacer()
Link("@mczachurski", destination: URL(string: "https://mastodon.social/@mczachurski")!)
.font(.footnote)
}
HStack {
VStack(alignment: .leading) {
Text("settings.title.follow", comment: "Follow me")
Text("settings.title.pixelfedAccount", comment: "Pixelfed account")
.font(.footnote)
.foregroundColor(.lightGrayColor)
}
Spacer()
Link("@mczachurski", destination: URL(string: "https://pixelfed.social/@mczachurski")!)
.font(.footnote)
}
}
}
}

View File

@ -196,7 +196,7 @@ struct UserProfileView: View {
Divider()
NavigationLink(value: RouteurDestinations.favourites) {
Label(NSLocalizedString("userProfile.title.favourites", comment: "Favourites"), systemImage: "hand.thumbsup")
Label(NSLocalizedString("userProfile.title.favourites", comment: "Favourites"), systemImage: "star")
}
NavigationLink(value: RouteurDestinations.bookmarks) {

View File

@ -24,7 +24,7 @@ struct FavouriteTouch: View {
.foregroundColor(.white.opacity(0.75))
.scaleEffect(CGFloat(showCircle))
Image(systemName: "hand.thumbsup.fill")
Image(systemName: "star.fill")
.font(.system(size: 26))
.foregroundColor(.black.opacity(0.4))
.clipShape(Rectangle().offset(y: CGFloat(showThumb)))

View File

@ -63,7 +63,7 @@ struct InteractionRow: View {
await self.favourite()
} label: {
HStack(alignment: .center) {
Image(systemName: self.favourited ? "hand.thumbsup.fill" : "hand.thumbsup")
Image(systemName: self.favourited ? "star.fill" : "star")
Text("\(self.favouritesCount)")
.font(.caption)
}
@ -85,7 +85,7 @@ struct InteractionRow: View {
}
NavigationLink(value: RouteurDestinations.accounts(listType: .favourited(entityId: statusModel.id))) {
Label("status.title.favouritedBy", systemImage: "hand.thumbsup")
Label("status.title.favouritedBy", systemImage: "star")
}
if let url = statusModel.url {
@ -175,7 +175,7 @@ struct InteractionRow: View {
ToastrService.shared.showSuccess(self.favourited
? NSLocalizedString("status.title.favourited", comment: "Favourited")
: NSLocalizedString("status.title.unfavourited", comment: "Unfavourited"), imageSystemName: "hand.thumbsup.fill")
: NSLocalizedString("status.title.unfavourited", comment: "Unfavourited"), imageSystemName: "star.fill")
} catch {
ErrorService.shared.handle(error, message: "status.error.favouriteFailed", showToastr: true)
}

View File

@ -74,7 +74,7 @@ public class TextModel: NSObject, ObservableObject {
.underlineColor: UIColor.clear],
range: NSRange(location: 0, length: text.string.utf16.count))
let hashtagPattern = "(#+[a-zA-Z0-9(_)]{1,})"
let hashtagPattern = "(#+[\\w0-9(_)]{1,})"
let mentionPattern = "(@+[a-zA-Z0-9(_).-]{1,})"
let urlPattern = "(?i)https?://(?:www\\.)?\\S+(?:/|\\b)"