summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Steinmetz <richard@steinmetz.cloud>2023-10-17 14:21:19 +0200
committerGitHub <noreply@github.com>2023-10-17 14:21:19 +0200
commit8d06b840c30b1d3ad6898566cc3172cf8c5c4c4c (patch)
treeb68c3a849f44ccd0419aff4a8ae26f4025eb3ee7
parent9f98bf3ee8fd0bb352bf9ece0857e6364e343c23 (diff)
parent1910947e55f40cee0bf530531309bb8460f2665b (diff)
Merge pull request #3656 from nextcloud/bug/3651/update-matching-score-2
-rw-r--r--src/components/ContactDetails/ContactDetailsProperty.vue28
-rw-r--r--src/utils/matchTypes.d.ts36
-rw-r--r--src/utils/matchTypes.ts50
-rw-r--r--tests/javascript/utils/matchTypes.test.ts133
4 files changed, 224 insertions, 23 deletions
diff --git a/src/components/ContactDetails/ContactDetailsProperty.vue b/src/components/ContactDetails/ContactDetailsProperty.vue
index 254911e2..3cfae91c 100644
--- a/src/components/ContactDetails/ContactDetailsProperty.vue
+++ b/src/components/ContactDetails/ContactDetailsProperty.vue
@@ -55,6 +55,7 @@ import PropertyText from '../Properties/PropertyText.vue'
import PropertyMultipleText from '../Properties/PropertyMultipleText.vue'
import PropertyDateTime from '../Properties/PropertyDateTime.vue'
import PropertySelect from '../Properties/PropertySelect.vue'
+import { matchTypes } from '../../utils/matchTypes.ts'
export default {
name: 'ContactDetailsProperty',
@@ -247,29 +248,10 @@ export default {
// we only use uppercase strings
.map(str => str.toUpperCase())
- // Compare array and score them by how many matches they have to the selected type
- // sorting directly is cleaner but slower
- // https://jsperf.com/array-map-and-intersection-perf
- const matchingTypes = this.propModel.options
- .map(type => {
- let score = 0
- const types = type.id.split(',') // "WORK,HOME" => ['WORK', 'HOME']
-
- if (types.length === selectedType.length) {
- // additional point for same length
- score++
- }
-
- const intersection = types.filter(value => selectedType.includes(value))
- score = score + intersection.length
-
- return { type, score }
- })
-
- // Sort by score, filtering out the null score and selecting the first match
- const matchingType = matchingTypes
- .sort((a, b) => b.score - a.score)
- .filter(type => type.score > 0)[0]
+ const matchingType = matchTypes(
+ selectedType,
+ this.propModel.options,
+ )
if (matchingType) {
return matchingType.type
diff --git a/src/utils/matchTypes.d.ts b/src/utils/matchTypes.d.ts
new file mode 100644
index 00000000..18804351
--- /dev/null
+++ b/src/utils/matchTypes.d.ts
@@ -0,0 +1,36 @@
+/**
+ * @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @author Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * Match a list of types against the available types
+ *
+ * @param {Array<string>} selectedTypes
+ * @param {Array<{id: string, name: string}>} options
+ */
+export declare function matchTypes(selectedTypes: Array<string>, options: Array<{
+ id: string;
+ name: string;
+}>): {
+ type: {
+ id: string;
+ name: string;
+ };
+ score: number;
+} | undefined;
diff --git a/src/utils/matchTypes.ts b/src/utils/matchTypes.ts
new file mode 100644
index 00000000..d0652192
--- /dev/null
+++ b/src/utils/matchTypes.ts
@@ -0,0 +1,50 @@
+/**
+ * @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @author Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Match a list of types against the available types
+ *
+ * @param {Array<string>} selectedTypes
+ * @param {Array<{id: string, name: string}>} options
+ */
+export function matchTypes(selectedTypes: Array<string>, options: Array<{id: string, name: string}>) {
+ const items = options.map(option => {
+ let score = 0
+ const types = option.id.split(',') // "WORK,HOME" => ['WORK', 'HOME']
+
+ const intersection = types.filter(value => selectedTypes.includes(value))
+ score = score + intersection.length
+
+ if (selectedTypes.length === types.length && selectedTypes.length === intersection.length) {
+ score++
+ }
+
+ return {
+ type: option,
+ score,
+ }
+ })
+
+ return items
+ .filter(value => value.score > 0)
+ .sort((a, b) => b.score - a.score)
+ .shift()
+}
diff --git a/tests/javascript/utils/matchTypes.test.ts b/tests/javascript/utils/matchTypes.test.ts
new file mode 100644
index 00000000..5fe9c0c4
--- /dev/null
+++ b/tests/javascript/utils/matchTypes.test.ts
@@ -0,0 +1,133 @@
+/**
+ * @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @author Daniel Kesselberg <mail@danielkesselberg.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+import { matchTypes } from '../../../src/utils/matchTypes'
+import rfcProps from '../../../src/models/rfcProps.js'
+
+describe('utils/matchTypes test suite', () => {
+
+ describe('impp', () => {
+ it('matches', () => {
+ const selectedTypes = ['XMPP']
+
+ const match = matchTypes(
+ selectedTypes,
+ rfcProps.properties.impp.options,
+ )
+
+ expect(match).toMatchObject({
+ type: { id: 'XMPP', name: 'XMPP' },
+ score: 2,
+ })
+ })
+
+ it('does not match', () => {
+ const selectedTypes = ['TEST']
+
+ const match = matchTypes(
+ selectedTypes,
+ rfcProps.properties.impp.options,
+ )
+
+ expect(match).toBeUndefined()
+ })
+ })
+
+ describe('tel', () => {
+ it('complete match, one type', () => {
+ const selectedTypes = ['VOICE']
+
+ const match = matchTypes(
+ selectedTypes,
+ rfcProps.properties.tel.options,
+ )
+
+ expect(match).toMatchObject({
+ type: { id: 'VOICE', name: 'Voice' },
+ score: 2,
+ })
+ })
+
+ it('complete match, two types', () => {
+ const selectedTypes = ['HOME', 'VOICE']
+
+ const match = matchTypes(
+ selectedTypes,
+ rfcProps.properties.tel.options,
+ )
+
+ expect(match).toMatchObject({
+ type: { id: 'HOME,VOICE', name: 'Home' },
+ score: 3,
+ })
+ })
+
+ it('partial match, two types', () => {
+ const selectedTypes = ['HOME', 'VOICE']
+
+ const options = [
+ { id: 'HOME,VOICE,TEST', name: 'Home' },
+ { id: 'HOME,VOICE', name: 'Home' },
+ { id: 'HOME', name: 'Home' },
+ { id: 'WORK,VOICE,TEST', name: 'Work' },
+ { id: 'WORK,VOICE', name: 'Work' },
+ { id: 'WORK', name: 'Work' },
+ { id: 'VOICE', name: 'Voice' },
+ { id: 'TEST', name: 'Test' },
+ ]
+
+ const match = matchTypes(
+ selectedTypes,
+ options,
+ )
+
+ expect(match).toMatchObject({
+ type: { id: 'HOME,VOICE', name: 'Home' },
+ score: 3,
+ })
+ })
+
+ it('does not match', () => {
+ const selectedType = ['TEST']
+
+ const match = matchTypes(
+ selectedType,
+ rfcProps.properties.tel.options,
+ )
+
+ expect(match).toBeUndefined()
+ })
+ })
+
+ describe('misc', () => {
+ it('empty list', () => {
+ const selectedType = ['TEST']
+
+ const match = matchTypes(
+ selectedType,
+ [],
+ )
+
+ expect(match).toBeUndefined()
+ })
+ })
+})