Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
0e859cb
Set Design Requires Compatibility for iOS 26 to true
mvasilak Sep 9, 2025
7c44b07
Update icon for iOS 26
mvasilak Sep 10, 2025
79fff4a
Preserve old icon in iOS versions older than 26
mvasilak Sep 11, 2025
24bcb6e
Add separate beta argument for triggering beta Xcode cloud workflow
mvasilak Sep 10, 2025
85e7552
Update icon for iOS 26
mvasilak Sep 13, 2025
541d3c0
Fix CitationController crash regression when an error occurs
mvasilak Sep 16, 2025
a155dfa
Fix iOS 26 item detail editing
mvasilak Sep 18, 2025
a9dd452
Fix support for file editing (#1174)
mvasilak Sep 18, 2025
a77e8f4
Fix PDF reader memory leak (#1176)
mvasilak Sep 22, 2025
a5a90dc
Update dependencies (#1177)
mvasilak Sep 22, 2025
bd2826e
Improved api error logging (#1180)
michalrentka Sep 30, 2025
5154cbd
Improve PDF document load (#1179)
mvasilak Sep 30, 2025
9d9a3a8
Improve annotation page label default value (#1178)
mvasilak Sep 30, 2025
9b1f698
Show sciencedirect cloudflare challenge to user (#1181)
mvasilak Oct 6, 2025
5004706
Improve item count observation (#1182)
mvasilak Oct 7, 2025
571dddd
Improve note editor action handler updates (#1183)
mvasilak Oct 8, 2025
d47c726
Fix PDF reader top anchor for iOS 26 (#1184)
mvasilak Oct 12, 2025
7e5db00
Fix CollectionsActionHandler items changes observation
mvasilak Oct 12, 2025
17bef83
Bump project iOS deployment target
mvasilak Jul 22, 2025
c16286b
Fix PDFAnnotationsViewController setupToolbar regression
mvasilak Jul 23, 2025
f31533f
Fix title and filename of PDF attachments added to an item (#1187)
mvasilak Oct 17, 2025
9063a22
Allow retrieve metadata for remote standalone PDFs (#1188)
mvasilak Oct 17, 2025
55331a8
Fix potential crashes (#1189)
mvasilak Oct 17, 2025
3016deb
Merge remote-tracking branch 'origin/2.0'
mvasilak Nov 4, 2025
e9b25b6
Update SPM dependencies
mvasilak Nov 4, 2025
09c4184
Updated translators & styles
michalrentka Nov 6, 2025
342d2d4
Fixed epub initial cfi value (#1203)
michalrentka Nov 6, 2025
4e19f40
Place intra document navigation buttons below sidebar for compact width
mvasilak Nov 20, 2025
4622a67
Update SPM dependencies
mvasilak Nov 20, 2025
112b122
Fix bugs (#1209)
mvasilak Nov 24, 2025
f4242b1
Fix share extension background upload delegate (#1207)
mvasilak Nov 24, 2025
9b05c54
Possible fix for ArrayEncoding json serialization (#1211)
michalrentka Nov 25, 2025
068c21e
Added preselect annotation option to html/epub reader (#1208)
michalrentka Nov 25, 2025
a1e419a
Hide full sync option behind multi-tap gesture (#1214)
michalrentka Dec 4, 2025
d5f0f0f
Improve PDF loading logging
mvasilak Dec 7, 2025
ceea182
Add debug reader item action for debug builds (#1213)
mvasilak Dec 8, 2025
b0960c4
Update reader settings preferred content size when needed (#1215)
mvasilak Dec 8, 2025
92c3ac7
Removed arxiv.org ATS exception (#1216)
michalrentka Dec 12, 2025
50950c6
Added the ability to edit order or hide annotation tools (#1217)
michalrentka Dec 16, 2025
d863046
Speech for PDF documents (#1131)
michalrentka Dec 17, 2025
e89dd8e
Tweak localizable strings
dstillman Dec 23, 2025
6ed3b50
Fix typo in Add by Identifier placeholder
dstillman Dec 24, 2025
3631bcb
Additional string tweaks
dstillman Dec 24, 2025
6708119
Improve PDF worker controller (#1218)
mvasilak Dec 24, 2025
da50212
Make speech feature respect gate and disable it
mvasilak Dec 24, 2025
cff7131
Update SPM dependencies
mvasilak Dec 24, 2025
743e3fc
Fix build bug
mvasilak Dec 24, 2025
894d5cd
Add ePub in allowed attachment picker types
mvasilak Dec 29, 2025
d523083
Fix regression
mvasilak Dec 29, 2025
d9ea5ba
Move zero case for items.collections_selected to a separate string
mvasilak Dec 29, 2025
d96567f
Update github actions (#1223)
mvasilak Dec 29, 2025
b02228e
Allow any type in attachment picker
mvasilak Dec 29, 2025
c8f9f1b
Fix dynamic type font size (#1225)
mvasilak Jan 5, 2026
f849e44
Add locales from Transifex (#1226)
dstillman Jan 8, 2026
850237d
Guard when migrating from an old schema without the "changes" propert…
mvasilak Jan 8, 2026
0d47cc4
Fix swiftlint violations
mvasilak Jan 9, 2026
2e441d7
Update locales from Transifex
dstillman Jan 12, 2026
6f2f9ae
Add English strings for missing strings in Localizable.stringsdict
dstillman Jan 12, 2026
2c6a742
Improve tag filter action handler (#1230)
mvasilak Jan 13, 2026
99bc0b3
Improve share extension request cookies (#1229)
mvasilak Jan 13, 2026
f4022fe
Update schema to version 39
mvasilak Jan 9, 2026
60e52a3
Show collections and library in item detail (#1227)
michalrentka Jan 14, 2026
be39c99
Updated html/epub reader
michalrentka Jan 14, 2026
67a582c
Fixed translation HTTP.request for document parsing Atom feeds as HTM…
michalrentka Jan 14, 2026
bb4a84f
Update schema to version 40
mvasilak Jan 15, 2026
c98581f
Fix loading initial pdf page if not yet synced (#1233)
michalrentka Jan 16, 2026
0bf8073
Update SPM dependencies
mvasilak Jan 20, 2026
2d297bb
Fix Sync Actions tests
mvasilak Feb 10, 2026
8a61a24
Improve share extension handling for web dav errors (#1236)
mvasilak Feb 13, 2026
b1a1cdb
Fix bugs (#1238)
mvasilak Feb 17, 2026
371286e
Update SPM dependencies (#1239)
mvasilak Feb 17, 2026
f187305
Fix sync error show item (#1240)
mvasilak Feb 18, 2026
2401cb9
Fix SplitAnnotationsDbRequest typo bug
mvasilak Feb 20, 2026
1386145
Add support for multiple open items
mvasilak Sep 22, 2023
79482a9
Improve OpenItemsController download dispose bag handling
mvasilak Jan 14, 2026
1367bfb
Add in current open item menu other item notes and attachments sub-menu
mvasilak Jan 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 6 additions & 6 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ on:
jobs:
tests:
name: Run Xcode Tests
runs-on: macos-15
runs-on: macos-26
strategy:
matrix:
xcode: ["16.3"]
ios_version: ["18.4"]
device_name: ["iPhone 16"]
xcode: ["26.2"]
ios_version: ["26.2"]
device_name: ["iPhone 17"]

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
submodules: recursive

Expand All @@ -41,7 +41,7 @@ jobs:
xcode-version: ${{ matrix.xcode }}

- name: Clean & Build & Test
uses: sersoft-gmbh/xcodebuild-action@v3.1
uses: sersoft-gmbh/xcodebuild-action@v4.0
with:
project: Zotero.xcodeproj
scheme: Zotero
Expand Down
31 changes: 31 additions & 0 deletions .tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[main]
host = https://app.transifex.com
lang_map = af_ZA: af, am_ET: am, bg_BG: bg, bn_BD: bn, ca: ca, cs_CZ: cs, da_DK: da, el_GR: el, en_GB: en-GB, es_AR: es-AR, es_ES: es, et_EE: et, eu_ES: eu, fi_FI: fi, fr_FR: fr, gl_ES: gl, he_IL: he, hr_HR: hr, hu_HU: hu, id_ID: id, is_IS: is, it_IT: it, ja_JP: ja, kn_IN: kn, ko_KR: ko, lt_LT: lt, mn_MN: mn, nb_NO: nb-NO, nl_NL: nl, nn_NO: nn-NO, pl_PL: pl, pt_BR: pt-BR, pt_PT: pt-PT, ro_RO: ro, ru_RU: ru, sk_SK: sk, sl_SI: sl, sr_RS: sr, sv_SE: sv, th_TH: th, tr_TR: tr, uk_UA: uk, vi_VN: vi, yue_CN: yue-Hans, zh_CN: zh-Hans, zh_TW: zh-Hant

[o:zotero:p:zotero-mobile:r:infopliststrings]
file_filter = Zotero/Assets/<lang>.lproj/InfoPList.strings
source_file = Zotero/Assets/en.lproj/InfoPList.strings
type = STRINGS_UTF8
minimum_perc = 0
resource_name = InfoPlist.strings
replace_edited_strings = false
keep_translations = false

[o:zotero:p:zotero-mobile:r:localizablestrings]
file_filter = ./Zotero/Assets/<lang>.lproj/Localizable.strings
source_file = ./Zotero/Assets/en.lproj/Localizable.strings
type = STRINGS_UTF8
minimum_perc = 0
resource_name = Localizable.strings
replace_edited_strings = false
keep_translations = false

[o:zotero:p:zotero-mobile:r:localizablestringsdict]
file_filter = ./Zotero/Assets/<lang>.lproj/Localizable.stringsdict
source_file = ./Zotero/Assets/en.lproj/Localizable.stringsdict
type = STRINGSDICT
minimum_perc = 0
resource_name = Localizable.stringsdict
replace_edited_strings = false
keep_translations = false

2 changes: 1 addition & 1 deletion FeatureGates.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) FEATURE_GATE_HTML_EPUB_READER FEATURE_GATE_PDF_WORKER
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) FEATURE_GATE_HTML_EPUB_READER FEATURE_GATE_PDF_WORKER FEATURE_GATE_MULTIPLE_OPEN_ITEMS
28 changes: 24 additions & 4 deletions ZShare/Assets/translation/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ Zotero.HTTP = new function() {
this.message = `HTTP request has timed out after ${ms}ms`;
};
this.TimeoutError.prototype = Object.create(Error.prototype);
this.VALID_DOM_PARSER_CONTENT_TYPES = new Set([
"text/html",
"text/xml",
"application/xml",
"application/xhtml+xml",
"image/svg+xml"
]);

/**
* Get a promise for a HTTP request
Expand Down Expand Up @@ -104,10 +111,7 @@ Zotero.HTTP = new function() {
case "document":
let parser = new DOMParser();
var contentType = headers["content-type"];
if (contentType != 'application/xml' && contentType != 'text/xml') {
contentType = 'text/html';
}
let doc = parser.parseFromString(responseText, contentType);
let doc = parser.parseFromString(responseText, this.determineDOMParserContentType(contentType));
response = doc;
break;

Expand Down Expand Up @@ -305,4 +309,20 @@ Zotero.HTTP = new function() {
};
return deferred.promise;
};

/**
* @param contentType {String}
* @returns {DOMParserSupportedType}
*/
this.determineDOMParserContentType = function(contentType) {
if (!contentType) {
return "text/html";
} else if (Zotero.HTTP.VALID_DOM_PARSER_CONTENT_TYPES.has(contentType)) {
return contentType;
} else if (contentType.includes('xml')) {
return "text/xml";
} else {
return "text/html";
}
}
}
4 changes: 2 additions & 2 deletions ZShare/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
</dict>
<key>NSMicrophoneUsageDescription</key>
<string>Access to microphone</string>
<key>UIDesignRequiresCompatibility</key>
<true/>
<key>UIDesignRequiresCompatibility</key>
<true/>
</dict>
</plist>
9 changes: 8 additions & 1 deletion ZShare/View Controllers/ShareViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ final class ShareViewController: UIViewController {
private func updateNavigationItems(for state: ExtensionViewModel.State.AttachmentState, isSubmitting: Bool) {
if let error = state.error {
switch error {
case .quotaLimit, .webDavFailure, .apiFailure, .forbidden:
case .quotaLimit, .webDavFailure, .webDavUnauthorized, .webDavForbidden, .apiFailure, .forbidden:
self.navigationItem.leftBarButtonItem = nil
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(ShareViewController.cancel))
return
Expand Down Expand Up @@ -475,6 +475,12 @@ final class ShareViewController: UIViewController {
case .webDavNotVerified:
return L10n.Errors.Shareext.webdavNotVerified

case .webDavUnauthorized:
return L10n.Errors.Settings.Webdav.unauthorized

case .webDavForbidden:
return L10n.Errors.Settings.Webdav.forbidden

case .cantLoadSchema:
return L10n.Errors.Shareext.cantLoadSchema

Expand Down Expand Up @@ -503,6 +509,7 @@ final class ShareViewController: UIViewController {

case .noSuccessfulTranslators:
return nil

case .cantFindFile, .webExtractionMissingJs: // should never happen
return L10n.Errors.Shareext.missingBaseFiles

Expand Down
51 changes: 51 additions & 0 deletions ZShare/ViewModels/ExtensionViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ final class ExtensionViewModel {
case quotaLimit(LibraryIdentifier)
case forbidden(LibraryIdentifier)
case webDavNotVerified
case webDavUnauthorized
case webDavForbidden
case webDavFailure
case md5Missing
case mtimeMissing
Expand Down Expand Up @@ -106,6 +108,8 @@ final class ExtensionViewModel {
.webViewError,
.quotaLimit,
.webDavNotVerified,
.webDavUnauthorized,
.webDavForbidden,
.webDavFailure:
return false
}
Expand Down Expand Up @@ -164,6 +168,7 @@ final class ExtensionViewModel {
default:
return true
}

default: return false
}
}
Expand Down Expand Up @@ -427,6 +432,7 @@ final class ExtensionViewModel {
case .itemWithAttachment(_, _, let file), .file(let file, _):
// Remove temporary local file if it exists
try? self.fileStorage.remove(file)

case .item: break
}
}
Expand Down Expand Up @@ -1023,6 +1029,7 @@ final class ExtensionViewModel {
switch self.state.attachmentState {
case .downloading:
self.state.attachmentState = .downloading(progress.fractionCompleted)

default: break
}
})
Expand Down Expand Up @@ -1257,6 +1264,24 @@ final class ExtensionViewModel {
if let error = error as? State.AttachmentState.Error {
return error
}
if let error = error as? WebDavError.Verification {
DDLogError("ExtensionViewModel: webdav verification failed - \(error)")
return .webDavNotVerified
}
if let error = error as? WebDavError.Upload {
DDLogError("ExtensionViewModel: webdav upload failed - \(error)")
switch error {
case .cantCreatePropData:
return .webDavFailure

case .apiError(let alamoError, _):
return webDavError(from: alamoError)
}
}
if let error = error as? WebDavError.Download {
DDLogError("ExtensionViewModel: webdav download failed - \(error)")
return .webDavFailure
}
if let error = error as? Parsing.Error {
DDLogError("ExtensionViewModel: could not parse item - \(error)")
return .parseError(error)
Expand Down Expand Up @@ -1296,6 +1321,32 @@ final class ExtensionViewModel {
}
return .unknown

func webDavError(from error: AFError) -> State.AttachmentState.Error {
switch error {
case .responseValidationFailed(let reason):
switch reason {
case .unacceptableStatusCode(let code):
switch code {
case 401:
return .webDavUnauthorized

case 403:
return .webDavForbidden

default:
break
}

default:
break
}

default:
break
}
return .webDavFailure
}

func alamoErrorRequiresAbort(_ error: AFError, url: URL?, libraryId: LibraryIdentifier?) -> State.AttachmentState.Error {
let defaultError: State.AttachmentState.Error = (url?.absoluteString ?? ApiConstants.baseUrlString).contains(ApiConstants.baseUrlString) ? .apiFailure : .webDavFailure
switch error {
Expand Down
Loading