Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support updatable dictionaries #1174

Merged
merged 22 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ext/css/settings.css
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,7 @@ input[type=number].dictionary-priority {
margin-right: 0.5em;
}
.dictionary-outdated-button,
.dictionary-update-available,
.dictionary-integrity-button {
--button-content-color: transparent;
--button-border-color: transparent;
Expand Down
22 changes: 19 additions & 3 deletions ext/data/schemas/dictionary-index-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"revision": {
"type": "string",
"description": "Revision of the dictionary. This value is only used for displaying information."
"description": "Revision of the dictionary. This value is displayed, and used to check for dictionary updates."
StefanVukovic99 marked this conversation as resolved.
Show resolved Hide resolved
},
"sequenced": {
"type": "boolean",
Expand All @@ -42,9 +42,22 @@
"type": "string",
"description": "Creator of the dictionary."
},
"isUpdatable": {
"type": "boolean",
"const": true,
"description": "Whether this dictionary contains links to its latest version."
},
"indexUrl": {
"type": "string",
"description": "URL for the index file of the latest revision of the dictionary, used to check for updates."
},
"downloadUrl": {
jamesmaa marked this conversation as resolved.
Show resolved Hide resolved
"type": "string",
"description": "URL for the download of the latest revision of the dictionary."
},
"url": {
"type": "string",
"description": "URL for the source of the dictionary."
"description": "URL for the source of the dictionary, displayed in the dictionary details."
},
"description": {
"type": "string",
Expand Down Expand Up @@ -101,5 +114,8 @@
{
"required": ["version"]
}
]
],
"dependencies": {
"isUpdatable": ["indexUrl", "downloadUrl"]
}
}
27 changes: 27 additions & 0 deletions ext/js/dictionary/dictionary-data-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,33 @@ export function isNonNounVerbOrAdjective(wordClasses) {
return isVerbOrAdjective && !(isSuruVerb && isNoun);
}

/**
* @param {string} current
* @param {string} latest
* @returns {boolean}
*/
export function compareRevisions(current, latest) {
const simpleVersionTest = /^(\d+\.)*\d+$/; // dot-separated integers, so 4.7 or 24.1.1.1 are ok, 1.0.0-alpha is not
if (!simpleVersionTest.test(current) || !simpleVersionTest.test(latest)) {
return current < latest;
}

const currentParts = current.split('.').map((part) => Number.parseInt(part, 10));
const latestParts = latest.split('.').map((part) => Number.parseInt(part, 10));

if (currentParts.length !== latestParts.length) {
return current < latest;
}

for (let i = 0; i < currentParts.length; i++) {
if (currentParts[i] !== latestParts[i]) {
return currentParts[i] < latestParts[i];
}
}

return false;
}

// Private

/**
Expand Down
32 changes: 30 additions & 2 deletions ext/js/dictionary/dictionary-importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ export class DictionaryImporter {
* @param {import('dictionary-data').Index} index
* @param {import('dictionary-importer').SummaryDetails} details
* @returns {import('dictionary-importer').Summary}
* @throws {Error}
*/
_createSummary(dictionaryTitle, version, index, details) {
const indexSequenced = index.sequenced;
Expand All @@ -327,18 +328,45 @@ export class DictionaryImporter {
styles,
};

const {author, url, description, attribution, frequencyMode, sourceLanguage, targetLanguage} = index;
const {author, url, description, attribution, frequencyMode, isUpdatable, sourceLanguage, targetLanguage} = index;
if (typeof author === 'string') { summary.author = author; }
if (typeof url === 'string') { summary.url = url; }
if (typeof description === 'string') { summary.description = description; }
if (typeof attribution === 'string') { summary.attribution = attribution; }
if (typeof frequencyMode === 'string') { summary.frequencyMode = frequencyMode; }
if (typeof sourceLanguage === 'string') { summary.sourceLanguage = sourceLanguage; }
if (typeof targetLanguage === 'string') { summary.targetLanguage = targetLanguage; }

if (typeof isUpdatable === 'boolean') {
const {indexUrl, downloadUrl} = index;
if (!isUpdatable || !this._validateUrl(indexUrl) || !this._validateUrl(downloadUrl)) {
StefanVukovic99 marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('Invalid index data for updatable dictionary');
}
summary.isUpdatable = isUpdatable;
summary.indexUrl = indexUrl;
summary.downloadUrl = downloadUrl;
}
return summary;
}

/**
* @param {string|undefined} string
* @returns {boolean}
*/
_validateUrl(string) {
if (typeof string !== 'string') {
return false;
}

let url;
try {
url = new URL(string);
} catch (_) {
return false;
}

return url.protocol === 'http:' || url.protocol === 'https:';
}

/**
* @param {import('ajv').ValidateFunction} schema
* @param {string} fileName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export class CollapsibleDictionaryController {
nameNode.textContent = dictionary;

/** @type {HTMLElement} */
const versionNode = querySelectorNotNull(node, '.dictionary-version');
const versionNode = querySelectorNotNull(node, '.dictionary-revision');
versionNode.textContent = version;

return querySelectorNotNull(node, '.definitions-collapsible');
Expand Down
Loading