|
| 1 | +<template> |
| 2 | +<div> |
| 3 | + <span>页游标 </span> |
| 4 | + <template v-for="([cursor, decoded], index) in Object.entries(decodedCursor)" :key="cursor"> |
| 5 | + <code v-tippy="tippyContent(decoded)" class="user-select-all">{{ cursor }}</code> |
| 6 | + <span v-if="Object.keys(decodedCursor).length !== index + 1" class="font-monospace text-muted">,</span> |
| 7 | + </template> |
| 8 | +</div> |
| 9 | +</template> |
| 10 | + |
| 11 | +<script setup lang="ts"> |
| 12 | +import 'core-js/actual/typed-array/from-base64'; |
| 13 | +
|
| 14 | +const { encodedCursor } = defineProps<{ encodedCursor: Cursor }>(); |
| 15 | +type DecodedCursorPart = Record<Cursor, { description: PostIDStr | `${PostTypeText}的排序字段值`, decoded: string | bigint }>; |
| 16 | +const tippyContent = (decoded: ObjValues<DecodedCursorPart>) => |
| 17 | + `<div class="d-flex align-items-center">${decoded.description}:<code class="user-select-all">${decoded.decoded}</code></div>`; |
| 18 | +
|
| 19 | +declare global { |
| 20 | + interface Uint8ArrayConstructor { |
| 21 | +
|
| 22 | + // https://github.com/tc39/proposal-arraybuffer-base64 |
| 23 | + // https://github.com/microsoft/TypeScript/pull/61696 |
| 24 | + // eslint-disable-next-line @typescript-eslint/method-signature-style |
| 25 | + fromBase64( |
| 26 | + string: string, |
| 27 | + options?: { // https://github.com/zloirock/core-js/blob/e10fa8dca0f5cea568b48e33e65fd11b06443b52/packages/core-js/internals/uint8-from-base64.js#L73-L80 |
| 28 | + alphabet?: 'base64' | 'base64url', |
| 29 | + lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial' |
| 30 | + }, |
| 31 | + ): Uint8Array<ArrayBuffer> |
| 32 | + } |
| 33 | +} |
| 34 | +const decodeCursor = (cursor: Cursor): DecodedCursorPart | ObjEmpty => { // https://github.com/n0099/open-tbm/blob/d37cd67974090ed3e64d1e5243b7474802a7431d/be/src/PostsQuery/CursorCodec.php#L22 |
| 35 | + if (cursor === '') |
| 36 | + return {}; |
| 37 | + const decodePart = (part: string) => { |
| 38 | + try { // https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript/79665302#79665302 |
| 39 | + return Uint8Array.fromBase64(part, { alphabet: 'base64url' }) |
| 40 | +
|
| 41 | + // https://stackoverflow.com/questions/24288111/why-does-32-not-result-in-0-in-javascript/79665336#79665336 |
| 42 | + // https://stackoverflow.com/questions/7334832/are-addition-and-bitwise-or-the-same-in-this-case |
| 43 | + // eslint-disable-next-line no-bitwise |
| 44 | + .reduce((acc, uint8, index) => acc | (BigInt(uint8) << BigInt(index * 8)), BigInt(0)); |
| 45 | + } catch (e: unknown) { |
| 46 | + if (e instanceof SyntaxError) // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 |
| 47 | + return part; |
| 48 | +
|
| 49 | + throw e; |
| 50 | + } |
| 51 | + }; |
| 52 | + const parts = cursor.split(','); |
| 53 | + if (parts.length !== 6) |
| 54 | + throw new Error('Cursor should have six parts.'); |
| 55 | +
|
| 56 | + return Object.assign({}, ...parts.map((part, index): DecodedCursorPart => |
| 57 | + ({ [part]: { description: index % 2 === 0 ? postID[index / 2] : `${postTypeText[(index - 1) / 2]}的排序字段值`, decoded: decodePart(part) } }))); |
| 58 | +}; |
| 59 | +const decodedCursor = computed(() => decodeCursor(encodedCursor)); |
| 60 | +</script> |
0 commit comments