From fe8966fc3e45b6a0e0ec74f09e254da23b41d02d Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 9 Sep 2022 20:44:52 -0500 Subject: [PATCH] Scaffold out timeline insertion modules --- .../features/timeline-insertion/abovefold.ts | 41 +++++++++++++++++++ .../features/timeline-insertion/index.ts | 0 .../features/timeline-insertion/linear.ts | 19 +++++++++ .../features/timeline-insertion/types.ts | 15 +++++++ package.json | 2 + yarn.lock | 10 +++++ 6 files changed, 87 insertions(+) create mode 100644 app/soapbox/features/timeline-insertion/abovefold.ts create mode 100644 app/soapbox/features/timeline-insertion/index.ts create mode 100644 app/soapbox/features/timeline-insertion/linear.ts create mode 100644 app/soapbox/features/timeline-insertion/types.ts diff --git a/app/soapbox/features/timeline-insertion/abovefold.ts b/app/soapbox/features/timeline-insertion/abovefold.ts new file mode 100644 index 000000000..78a8b6e6f --- /dev/null +++ b/app/soapbox/features/timeline-insertion/abovefold.ts @@ -0,0 +1,41 @@ +import seedrandom from 'seedrandom'; + +import type { PickAlgorithm } from './types'; + +type Opts = { + /** Randomization seed. */ + seed: string, + /** + * Start/end index of the slot by which one item will be randomly picked per page. + * + * Eg. `[3, 7]` will cause one item to be picked between the third and seventh indexes per page. + * + * `end` must be larger than `start`. + */ + range: [start: number, end: number], + /** Number of items in the page. */ + pageSize: number, +}; + +/** + * Algorithm to display items per-page. + * One item is randomly inserted into each page within the index range. + */ +const abovefoldAlgorithm: PickAlgorithm = (items, index, opts: Opts) => { + /** Current page of the index. */ + const page = Math.floor(((index + 1) / opts.pageSize) - 1); + /** Current index within the page. */ + const pageIndex = ((index + 1) % opts.pageSize) - 1; + /** RNG for the page. */ + const rng = seedrandom(`${opts.seed}-page-${page}`); + /** Index to insert the item. */ + const insertIndex = Math.floor(rng() * opts.range[1] - opts.range[0]) + opts.range[0]; + + if (pageIndex === insertIndex) { + return items[page]; + } +}; + +export { + abovefoldAlgorithm, +}; \ No newline at end of file diff --git a/app/soapbox/features/timeline-insertion/index.ts b/app/soapbox/features/timeline-insertion/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/app/soapbox/features/timeline-insertion/linear.ts b/app/soapbox/features/timeline-insertion/linear.ts new file mode 100644 index 000000000..923ef1207 --- /dev/null +++ b/app/soapbox/features/timeline-insertion/linear.ts @@ -0,0 +1,19 @@ +import type { PickAlgorithm } from './types'; + +type Opts = { + /** Number of iterations until the next item is picked. */ + interval: number, +}; + +/** Picks the next item every `interval` iterations. */ +const linearAlgorithm: PickAlgorithm = (items, index, opts: Opts) => { + const itemIndex = items ? Math.floor((index + 1) / opts.interval) % items.length : 0; + const item = items ? items[itemIndex] : undefined; + const showItem = (index + 1) % opts.interval === 0; + + return showItem ? item : undefined; +}; + +export { + linearAlgorithm, +}; \ No newline at end of file diff --git a/app/soapbox/features/timeline-insertion/types.ts b/app/soapbox/features/timeline-insertion/types.ts new file mode 100644 index 000000000..0b6fd6c0c --- /dev/null +++ b/app/soapbox/features/timeline-insertion/types.ts @@ -0,0 +1,15 @@ +/** + * Returns an item to insert at the index, or `undefined` if an item shouldn't be inserted. + */ +type PickAlgorithm = ( + /** Elligible candidates to pick. */ + items: D[], + /** Current iteration by which an item may be chosen. */ + index: number, + /** Implementation-specific opts. */ + opts: any +) => D | undefined; + +export { + PickAlgorithm, +}; \ No newline at end of file diff --git a/package.json b/package.json index eef743978..1ba60028b 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@types/react-swipeable-views": "^0.13.1", "@types/react-toggle": "^4.0.3", "@types/redux-mock-store": "^1.0.3", + "@types/seedrandom": "^3.0.2", "@types/semver": "^7.3.9", "@types/uuid": "^8.3.4", "array-includes": "^3.1.5", @@ -184,6 +185,7 @@ "resize-observer-polyfill": "^1.5.1", "sass": "^1.20.3", "sass-loader": "^13.0.0", + "seedrandom": "^3.0.5", "semver": "^7.3.2", "stringz": "^2.0.0", "substring-trie": "^1.0.2", diff --git a/yarn.lock b/yarn.lock index 1e2044863..a3cbcff2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2841,6 +2841,11 @@ dependencies: schema-utils "*" +"@types/seedrandom@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.2.tgz#7f30db28221067a90b02e73ffd46b6685b18df1a" + integrity sha512-YPLqEOo0/X8JU3rdiq+RgUKtQhQtrppE766y7vMTu8dGML7TVtZNiiiaC/hhU9Zqw9UYopXxhuWWENclMVBwKQ== + "@types/semver@^7.3.9": version "7.3.9" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc" @@ -10461,6 +10466,11 @@ scroll-behavior@^0.9.1: dom-helpers "^3.4.0" invariant "^2.2.4" +seedrandom@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" + integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"