merge in main
This commit is contained in:
@@ -31,6 +31,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'bsky-internal/use-exact-imports': 'error',
|
||||||
'bsky-internal/use-typed-gates': 'error',
|
'bsky-internal/use-typed-gates': 'error',
|
||||||
'simple-import-sort/imports': [
|
'simple-import-sort/imports': [
|
||||||
'warn',
|
'warn',
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {createFullHandle, makeValidHandle} from '../../src/lib/strings/handles'
|
|||||||
import {enforceLen} from '../../src/lib/strings/helpers'
|
import {enforceLen} from '../../src/lib/strings/helpers'
|
||||||
import {detectLinkables} from '../../src/lib/strings/rich-text-detection'
|
import {detectLinkables} from '../../src/lib/strings/rich-text-detection'
|
||||||
import {shortenLinks} from '../../src/lib/strings/rich-text-manip'
|
import {shortenLinks} from '../../src/lib/strings/rich-text-manip'
|
||||||
import {ago} from '../../src/lib/strings/time'
|
|
||||||
import {
|
import {
|
||||||
makeRecordUri,
|
makeRecordUri,
|
||||||
toNiceDomain,
|
toNiceDomain,
|
||||||
@@ -147,79 +146,6 @@ describe('makeRecordUri', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// FIXME: Reenable after fixing non-deterministic test.
|
|
||||||
describe.skip('ago', () => {
|
|
||||||
const oneYearDate = new Date(
|
|
||||||
new Date().setMonth(new Date().getMonth() - 11),
|
|
||||||
).setDate(new Date().getDate() - 28)
|
|
||||||
|
|
||||||
const inputs = [
|
|
||||||
1671461038,
|
|
||||||
'04 Dec 1995 00:12:00 GMT',
|
|
||||||
new Date(),
|
|
||||||
new Date().setSeconds(new Date().getSeconds() - 10),
|
|
||||||
new Date().setMinutes(new Date().getMinutes() - 10),
|
|
||||||
new Date().setHours(new Date().getHours() - 1),
|
|
||||||
new Date().setDate(new Date().getDate() - 1),
|
|
||||||
new Date().setDate(new Date().getDate() - 20),
|
|
||||||
new Date().setDate(new Date().getDate() - 25),
|
|
||||||
new Date().setDate(new Date().getDate() - 28),
|
|
||||||
new Date().setDate(new Date().getDate() - 29),
|
|
||||||
new Date().setDate(new Date().getDate() - 30),
|
|
||||||
new Date().setMonth(new Date().getMonth() - 1),
|
|
||||||
new Date(new Date().setMonth(new Date().getMonth() - 1)).setDate(
|
|
||||||
new Date().getDate() - 20,
|
|
||||||
),
|
|
||||||
new Date(new Date().setMonth(new Date().getMonth() - 1)).setDate(
|
|
||||||
new Date().getDate() - 25,
|
|
||||||
),
|
|
||||||
new Date(new Date().setMonth(new Date().getMonth() - 1)).setDate(
|
|
||||||
new Date().getDate() - 28,
|
|
||||||
),
|
|
||||||
new Date(new Date().setMonth(new Date().getMonth() - 1)).setDate(
|
|
||||||
new Date().getDate() - 29,
|
|
||||||
),
|
|
||||||
new Date().setMonth(new Date().getMonth() - 11),
|
|
||||||
new Date(new Date().setMonth(new Date().getMonth() - 11)).setDate(
|
|
||||||
new Date().getDate() - 20,
|
|
||||||
),
|
|
||||||
new Date(new Date().setMonth(new Date().getMonth() - 11)).setDate(
|
|
||||||
new Date().getDate() - 25,
|
|
||||||
),
|
|
||||||
oneYearDate,
|
|
||||||
]
|
|
||||||
const outputs = [
|
|
||||||
new Date(1671461038).toLocaleDateString(),
|
|
||||||
new Date('04 Dec 1995 00:12:00 GMT').toLocaleDateString(),
|
|
||||||
'now',
|
|
||||||
'10s',
|
|
||||||
'10m',
|
|
||||||
'1h',
|
|
||||||
'1d',
|
|
||||||
'20d',
|
|
||||||
'25d',
|
|
||||||
'28d',
|
|
||||||
'29d',
|
|
||||||
'1mo',
|
|
||||||
'1mo',
|
|
||||||
'1mo',
|
|
||||||
'1mo',
|
|
||||||
'2mo',
|
|
||||||
'2mo',
|
|
||||||
'11mo',
|
|
||||||
'11mo',
|
|
||||||
'11mo',
|
|
||||||
new Date(oneYearDate).toLocaleDateString(),
|
|
||||||
]
|
|
||||||
|
|
||||||
it('correctly calculates how much time passed, in a string', () => {
|
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
|
||||||
const result = ago(inputs[i])
|
|
||||||
expect(result).toEqual(outputs[i])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('makeValidHandle', () => {
|
describe('makeValidHandle', () => {
|
||||||
const inputs = [
|
const inputs = [
|
||||||
'test-handle-123',
|
'test-handle-123',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
rules: {
|
rules: {
|
||||||
'avoid-unwrapped-text': require('./avoid-unwrapped-text'),
|
'avoid-unwrapped-text': require('./avoid-unwrapped-text'),
|
||||||
|
'use-exact-imports': require('./use-exact-imports'),
|
||||||
'use-typed-gates': require('./use-typed-gates'),
|
'use-typed-gates': require('./use-typed-gates'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/* eslint-disable bsky-internal/use-exact-imports */
|
||||||
|
const BANNED_IMPORTS = [
|
||||||
|
'@fortawesome/free-regular-svg-icons',
|
||||||
|
'@fortawesome/free-solid-svg-icons',
|
||||||
|
]
|
||||||
|
|
||||||
|
exports.create = function create(context) {
|
||||||
|
return {
|
||||||
|
Literal(node) {
|
||||||
|
if (typeof node.value !== 'string') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (BANNED_IMPORTS.includes(node.value)) {
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
message:
|
||||||
|
'Import the specific thing you want instead of the entire package',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -272,6 +272,7 @@
|
|||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"**/zeed-dom": "0.10.9",
|
"**/zeed-dom": "0.10.9",
|
||||||
|
"**/zod": "3.23.8",
|
||||||
"**/expo-constants": "16.0.1",
|
"**/expo-constants": "16.0.1",
|
||||||
"**/expo-device": "6.0.2",
|
"**/expo-device": "6.0.2",
|
||||||
"@react-native/babel-preset": "0.74.1"
|
"@react-native/babel-preset": "0.74.1"
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import {describe, expect, it} from '@jest/globals'
|
||||||
|
import {MessageDescriptor} from '@lingui/core'
|
||||||
|
import {addDays, subDays, subHours, subMinutes, subSeconds} from 'date-fns'
|
||||||
|
|
||||||
|
import {dateDiff} from '../useTimeAgo'
|
||||||
|
|
||||||
|
const lingui: any = (obj: MessageDescriptor) => obj.message
|
||||||
|
|
||||||
|
const base = new Date('2024-06-17T00:00:00Z')
|
||||||
|
|
||||||
|
describe('dateDiff', () => {
|
||||||
|
it(`works with numbers`, () => {
|
||||||
|
expect(dateDiff(subDays(base, 3), Number(base), {lingui})).toEqual('3d')
|
||||||
|
})
|
||||||
|
it(`works with strings`, () => {
|
||||||
|
expect(dateDiff(subDays(base, 3), base.toString(), {lingui})).toEqual('3d')
|
||||||
|
})
|
||||||
|
it(`works with dates`, () => {
|
||||||
|
expect(dateDiff(subDays(base, 3), base, {lingui})).toEqual('3d')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`equal values return now`, () => {
|
||||||
|
expect(dateDiff(base, base, {lingui})).toEqual('now')
|
||||||
|
})
|
||||||
|
it(`future dates return now`, () => {
|
||||||
|
expect(dateDiff(addDays(base, 3), base, {lingui})).toEqual('now')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`values < 5 seconds ago return now`, () => {
|
||||||
|
const then = subSeconds(base, 4)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('now')
|
||||||
|
})
|
||||||
|
it(`values >= 5 seconds ago return seconds`, () => {
|
||||||
|
const then = subSeconds(base, 5)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('5s')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`values < 1 min return seconds`, () => {
|
||||||
|
const then = subSeconds(base, 59)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('59s')
|
||||||
|
})
|
||||||
|
it(`values >= 1 min return minutes`, () => {
|
||||||
|
const then = subSeconds(base, 60)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1m')
|
||||||
|
})
|
||||||
|
it(`minutes round down`, () => {
|
||||||
|
const then = subSeconds(base, 119)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1m')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`values < 1 hour return minutes`, () => {
|
||||||
|
const then = subMinutes(base, 59)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('59m')
|
||||||
|
})
|
||||||
|
it(`values >= 1 hour return hours`, () => {
|
||||||
|
const then = subMinutes(base, 60)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1h')
|
||||||
|
})
|
||||||
|
it(`hours round down`, () => {
|
||||||
|
const then = subMinutes(base, 119)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1h')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`values < 1 day return hours`, () => {
|
||||||
|
const then = subHours(base, 23)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('23h')
|
||||||
|
})
|
||||||
|
it(`values >= 1 day return days`, () => {
|
||||||
|
const then = subHours(base, 24)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1d')
|
||||||
|
})
|
||||||
|
it(`days round down`, () => {
|
||||||
|
const then = subHours(base, 47)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1d')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`values < 30 days return days`, () => {
|
||||||
|
const then = subDays(base, 29)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('29d')
|
||||||
|
})
|
||||||
|
it(`values >= 30 days return months`, () => {
|
||||||
|
const then = subDays(base, 30)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1mo')
|
||||||
|
})
|
||||||
|
it(`months round down`, () => {
|
||||||
|
const then = subDays(base, 59)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('1mo')
|
||||||
|
})
|
||||||
|
it(`values are rounded by increments of 30`, () => {
|
||||||
|
const then = subDays(base, 61)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('2mo')
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`values < 360 days return months`, () => {
|
||||||
|
const then = subDays(base, 359)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual('11mo')
|
||||||
|
})
|
||||||
|
it(`values >= 360 days return the earlier value`, () => {
|
||||||
|
const then = subDays(base, 360)
|
||||||
|
expect(dateDiff(then, base, {lingui})).toEqual(then.toLocaleDateString())
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import {useCallback, useMemo} from 'react'
|
||||||
|
import {msg, plural} from '@lingui/macro'
|
||||||
|
import {I18nContext, useLingui} from '@lingui/react'
|
||||||
|
import {differenceInSeconds} from 'date-fns'
|
||||||
|
|
||||||
|
export type TimeAgoOptions = {
|
||||||
|
lingui: I18nContext['_']
|
||||||
|
format?: 'long' | 'short'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGetTimeAgo() {
|
||||||
|
const {_} = useLingui()
|
||||||
|
return useCallback(
|
||||||
|
(
|
||||||
|
date: number | string | Date,
|
||||||
|
options?: Omit<TimeAgoOptions, 'lingui'>,
|
||||||
|
) => {
|
||||||
|
return dateDiff(date, Date.now(), {lingui: _, format: options?.format})
|
||||||
|
},
|
||||||
|
[_],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTimeAgo(
|
||||||
|
date: number | string | Date,
|
||||||
|
options?: Omit<TimeAgoOptions, 'lingui'>,
|
||||||
|
): string {
|
||||||
|
const timeAgo = useGetTimeAgo()
|
||||||
|
return useMemo(() => {
|
||||||
|
return timeAgo(date, {...options})
|
||||||
|
}, [date, options, timeAgo])
|
||||||
|
}
|
||||||
|
|
||||||
|
const NOW = 5
|
||||||
|
const MINUTE = 60
|
||||||
|
const HOUR = MINUTE * 60
|
||||||
|
const DAY = HOUR * 24
|
||||||
|
const MONTH_30 = DAY * 30
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the difference between `earlier` and `later` dates, formatted as a
|
||||||
|
* natural language string.
|
||||||
|
*
|
||||||
|
* - All month are considered exactly 30 days.
|
||||||
|
* - Dates assume `earlier` <= `later`, and will otherwise return 'now'.
|
||||||
|
* - Differences >= 360 days are returned as the "M/D/YYYY" string
|
||||||
|
* - All values round down
|
||||||
|
*/
|
||||||
|
export function dateDiff(
|
||||||
|
earlier: number | string | Date,
|
||||||
|
later: number | string | Date,
|
||||||
|
options: TimeAgoOptions,
|
||||||
|
): string {
|
||||||
|
const _ = options.lingui
|
||||||
|
const format = options?.format || 'short'
|
||||||
|
const long = format === 'long'
|
||||||
|
const diffSeconds = differenceInSeconds(new Date(later), new Date(earlier))
|
||||||
|
|
||||||
|
if (diffSeconds < NOW) {
|
||||||
|
return _(msg`now`)
|
||||||
|
} else if (diffSeconds < MINUTE) {
|
||||||
|
return `${diffSeconds}${
|
||||||
|
long ? ` ${plural(diffSeconds, {one: 'second', other: 'seconds'})}` : 's'
|
||||||
|
}`
|
||||||
|
} else if (diffSeconds < HOUR) {
|
||||||
|
const diff = Math.floor(diffSeconds / MINUTE)
|
||||||
|
return `${diff}${
|
||||||
|
long ? ` ${plural(diff, {one: 'minute', other: 'minutes'})}` : 'm'
|
||||||
|
}`
|
||||||
|
} else if (diffSeconds < DAY) {
|
||||||
|
const diff = Math.floor(diffSeconds / HOUR)
|
||||||
|
return `${diff}${
|
||||||
|
long ? ` ${plural(diff, {one: 'hour', other: 'hours'})}` : 'h'
|
||||||
|
}`
|
||||||
|
} else if (diffSeconds < MONTH_30) {
|
||||||
|
const diff = Math.floor(diffSeconds / DAY)
|
||||||
|
return `${diff}${
|
||||||
|
long ? ` ${plural(diff, {one: 'day', other: 'days'})}` : 'd'
|
||||||
|
}`
|
||||||
|
} else {
|
||||||
|
const diff = Math.floor(diffSeconds / MONTH_30)
|
||||||
|
if (diff < 12) {
|
||||||
|
return `${diff}${
|
||||||
|
long ? ` ${plural(diff, {one: 'month', other: 'months'})}` : 'mo'
|
||||||
|
}`
|
||||||
|
} else {
|
||||||
|
const str = new Date(earlier).toLocaleDateString()
|
||||||
|
|
||||||
|
if (long) {
|
||||||
|
return _(msg`on ${str}`)
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,70 +1,3 @@
|
|||||||
import {plural} from '@lingui/macro'
|
|
||||||
|
|
||||||
const NOW = 5
|
|
||||||
const MINUTE = 60
|
|
||||||
const HOUR = MINUTE * 60
|
|
||||||
const DAY = HOUR * 24
|
|
||||||
const MONTH_30 = DAY * 30
|
|
||||||
const MONTH = DAY * 30.41675 // This results in 365.001 days in a year, which is close enough for nearly all cases
|
|
||||||
export function ago(date: number | string | Date, long = false): string {
|
|
||||||
let ts: number
|
|
||||||
if (typeof date === 'string') {
|
|
||||||
ts = Number(new Date(date))
|
|
||||||
} else if (date instanceof Date) {
|
|
||||||
ts = Number(date)
|
|
||||||
} else {
|
|
||||||
ts = date
|
|
||||||
}
|
|
||||||
const diffSeconds = Math.floor((Date.now() - ts) / 1e3)
|
|
||||||
if (diffSeconds < NOW) {
|
|
||||||
return `now`
|
|
||||||
} else if (diffSeconds < MINUTE) {
|
|
||||||
return `${diffSeconds}${
|
|
||||||
long ? ` ${plural(diffSeconds, {one: 'second', other: 'seconds'})}` : 's'
|
|
||||||
}`
|
|
||||||
} else if (diffSeconds < HOUR) {
|
|
||||||
const diff = Math.floor(diffSeconds / MINUTE)
|
|
||||||
return `${diff}${
|
|
||||||
long ? ` ${plural(diff, {one: 'minute', other: 'minutes'})}` : 'm'
|
|
||||||
}`
|
|
||||||
} else if (diffSeconds < DAY) {
|
|
||||||
const diff = Math.floor(diffSeconds / HOUR)
|
|
||||||
return `${diff}${
|
|
||||||
long ? ` ${plural(diff, {one: 'hour', other: 'hours'})}` : 'h'
|
|
||||||
}`
|
|
||||||
} else if (diffSeconds < MONTH_30) {
|
|
||||||
const diff = Math.round(diffSeconds / DAY)
|
|
||||||
return `${diff}${
|
|
||||||
long ? ` ${plural(diff, {one: 'day', other: 'days'})}` : 'd'
|
|
||||||
}`
|
|
||||||
} else {
|
|
||||||
let months = diffSeconds / MONTH
|
|
||||||
if (months % 1 >= 0.9) {
|
|
||||||
months = Math.ceil(months)
|
|
||||||
} else {
|
|
||||||
months = Math.floor(months)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (months < 12) {
|
|
||||||
return `${months} ${
|
|
||||||
long
|
|
||||||
? plural(months, {
|
|
||||||
one: 'month',
|
|
||||||
other: 'months',
|
|
||||||
})
|
|
||||||
: 'mo'
|
|
||||||
}`
|
|
||||||
} else {
|
|
||||||
const str = new Date(ts).toLocaleDateString()
|
|
||||||
|
|
||||||
if (long) {
|
|
||||||
return `on ${str}`
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function niceDate(date: number | string | Date) {
|
export function niceDate(date: number | string | Date) {
|
||||||
const d = new Date(date)
|
const d = new Date(date)
|
||||||
return `${d.toLocaleDateString('en-us', {
|
return `${d.toLocaleDateString('en-us', {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import '@formatjs/intl-locale/polyfill'
|
import '@formatjs/intl-locale/polyfill'
|
||||||
import '@formatjs/intl-pluralrules/polyfill'
|
import '@formatjs/intl-pluralrules/polyfill-force' // Don't remove -force because detection is very slow
|
||||||
import '@formatjs/intl-pluralrules/locale-data/en'
|
import '@formatjs/intl-pluralrules/locale-data/en'
|
||||||
|
|
||||||
import {useEffect} from 'react'
|
import {useEffect} from 'react'
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
||||||
import {useTickEveryMinute} from '#/state/shell'
|
import {useTickEveryMinute} from '#/state/shell'
|
||||||
import {ago} from 'lib/strings/time'
|
|
||||||
|
|
||||||
export function TimeElapsed({
|
export function TimeElapsed({
|
||||||
timestamp,
|
timestamp,
|
||||||
children,
|
children,
|
||||||
timeToString = ago,
|
timeToString,
|
||||||
}: {
|
}: {
|
||||||
timestamp: string
|
timestamp: string
|
||||||
children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element
|
children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element
|
||||||
timeToString?: (timeElapsed: string) => string
|
timeToString?: (timeElapsed: string) => string
|
||||||
}) {
|
}) {
|
||||||
|
const ago = useGetTimeAgo()
|
||||||
|
const format = timeToString ?? ago
|
||||||
const tick = useTickEveryMinute()
|
const tick = useTickEveryMinute()
|
||||||
const [timeElapsed, setTimeAgo] = React.useState(() =>
|
const [timeElapsed, setTimeAgo] = React.useState(() => format(timestamp))
|
||||||
timeToString(timestamp),
|
|
||||||
)
|
|
||||||
|
|
||||||
const [prevTick, setPrevTick] = React.useState(tick)
|
const [prevTick, setPrevTick] = React.useState(tick)
|
||||||
if (prevTick !== tick) {
|
if (prevTick !== tick) {
|
||||||
setPrevTick(tick)
|
setPrevTick(tick)
|
||||||
setTimeAgo(timeToString(timestamp))
|
setTimeAgo(format(timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
return children({timeElapsed})
|
return children({timeElapsed})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {library} from '@fortawesome/fontawesome-svg-core'
|
import {library} from '@fortawesome/fontawesome-svg-core'
|
||||||
import {faAddressCard} from '@fortawesome/free-regular-svg-icons'
|
import {faAddressCard} from '@fortawesome/free-regular-svg-icons/faAddressCard'
|
||||||
import {faBell as farBell} from '@fortawesome/free-regular-svg-icons/faBell'
|
import {faBell as farBell} from '@fortawesome/free-regular-svg-icons/faBell'
|
||||||
import {faBookmark as farBookmark} from '@fortawesome/free-regular-svg-icons/faBookmark'
|
import {faBookmark as farBookmark} from '@fortawesome/free-regular-svg-icons/faBookmark'
|
||||||
import {faCalendar as farCalendar} from '@fortawesome/free-regular-svg-icons/faCalendar'
|
import {faCalendar as farCalendar} from '@fortawesome/free-regular-svg-icons/faCalendar'
|
||||||
@@ -25,8 +25,6 @@ import {faSquareCheck} from '@fortawesome/free-regular-svg-icons/faSquareCheck'
|
|||||||
import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus'
|
import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus'
|
||||||
import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan'
|
import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan'
|
||||||
import {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
|
import {faUser} from '@fortawesome/free-regular-svg-icons/faUser'
|
||||||
import {faFlask} from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import {faUniversalAccess} from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import {faAngleDown} from '@fortawesome/free-solid-svg-icons/faAngleDown'
|
import {faAngleDown} from '@fortawesome/free-solid-svg-icons/faAngleDown'
|
||||||
import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft'
|
import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft'
|
||||||
import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight'
|
import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight'
|
||||||
@@ -62,6 +60,7 @@ import {faExclamation} from '@fortawesome/free-solid-svg-icons/faExclamation'
|
|||||||
import {faEye} from '@fortawesome/free-solid-svg-icons/faEye'
|
import {faEye} from '@fortawesome/free-solid-svg-icons/faEye'
|
||||||
import {faFilter} from '@fortawesome/free-solid-svg-icons/faFilter'
|
import {faFilter} from '@fortawesome/free-solid-svg-icons/faFilter'
|
||||||
import {faFire} from '@fortawesome/free-solid-svg-icons/faFire'
|
import {faFire} from '@fortawesome/free-solid-svg-icons/faFire'
|
||||||
|
import {faFlask} from '@fortawesome/free-solid-svg-icons/faFlask'
|
||||||
import {faGear} from '@fortawesome/free-solid-svg-icons/faGear'
|
import {faGear} from '@fortawesome/free-solid-svg-icons/faGear'
|
||||||
import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe'
|
import {faGlobe} from '@fortawesome/free-solid-svg-icons/faGlobe'
|
||||||
import {faHand} from '@fortawesome/free-solid-svg-icons/faHand'
|
import {faHand} from '@fortawesome/free-solid-svg-icons/faHand'
|
||||||
@@ -97,6 +96,7 @@ import {faSignal} from '@fortawesome/free-solid-svg-icons/faSignal'
|
|||||||
import {faSliders} from '@fortawesome/free-solid-svg-icons/faSliders'
|
import {faSliders} from '@fortawesome/free-solid-svg-icons/faSliders'
|
||||||
import {faThumbtack} from '@fortawesome/free-solid-svg-icons/faThumbtack'
|
import {faThumbtack} from '@fortawesome/free-solid-svg-icons/faThumbtack'
|
||||||
import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
|
import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
|
||||||
|
import {faUniversalAccess} from '@fortawesome/free-solid-svg-icons/faUniversalAccess'
|
||||||
import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck'
|
import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck'
|
||||||
import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'
|
import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus'
|
||||||
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
|
import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers'
|
||||||
|
|||||||
+13
-11
@@ -1,18 +1,19 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types'
|
|
||||||
import {ScrollView} from '../com/util/Views'
|
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
|
||||||
import {Text} from '../com/util/text/Text'
|
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {getEntries} from '#/logger/logDump'
|
|
||||||
import {ago} from 'lib/strings/time'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
|
||||||
|
import {getEntries} from '#/logger/logDump'
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
||||||
|
import {s} from 'lib/styles'
|
||||||
|
import {Text} from '../com/util/text/Text'
|
||||||
|
import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
|
import {ScrollView} from '../com/util/Views'
|
||||||
|
|
||||||
export function LogScreen({}: NativeStackScreenProps<
|
export function LogScreen({}: NativeStackScreenProps<
|
||||||
CommonNavigatorParams,
|
CommonNavigatorParams,
|
||||||
@@ -22,6 +23,7 @@ export function LogScreen({}: NativeStackScreenProps<
|
|||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const setMinimalShellMode = useSetMinimalShellMode()
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
const [expanded, setExpanded] = React.useState<string[]>([])
|
const [expanded, setExpanded] = React.useState<string[]>([])
|
||||||
|
const timeAgo = useGetTimeAgo()
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
@@ -70,7 +72,7 @@ export function LogScreen({}: NativeStackScreenProps<
|
|||||||
/>
|
/>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<Text type="sm" style={[styles.ts, pal.textLight]}>
|
<Text type="sm" style={[styles.ts, pal.textLight]}>
|
||||||
{ago(entry.timestamp)}
|
{timeAgo(entry.timestamp)}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{expanded.includes(entry.id) ? (
|
{expanded.includes(entry.id) ? (
|
||||||
|
|||||||
@@ -22502,12 +22502,7 @@ zod-validation-error@^3.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.3.0.tgz#2cfe81b62d044e0453d1aa3ae7c32a2f36dde9af"
|
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.3.0.tgz#2cfe81b62d044e0453d1aa3ae7c32a2f36dde9af"
|
||||||
integrity sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==
|
integrity sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==
|
||||||
|
|
||||||
zod@^3.14.2, zod@^3.20.2:
|
zod@3.23.8, zod@^3.14.2, zod@^3.20.2, zod@^3.21.4, zod@^3.22.4:
|
||||||
version "3.22.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.2.tgz#3add8c682b7077c05ac6f979fea6998b573e157b"
|
|
||||||
integrity sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==
|
|
||||||
|
|
||||||
zod@^3.21.4, zod@^3.22.4:
|
|
||||||
version "3.23.8"
|
version "3.23.8"
|
||||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
|
||||||
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
|
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
|
||||||
|
|||||||
Reference in New Issue
Block a user