* add isLikedPressed flag to disallow like counter out of sync * create revertible helper for updateDataOptimistically * test implementation * Update updateDataOptimistically() and apply to reposts --------- Co-authored-by: Paul Frazee <pfrazee@gmail.com>zio/stable
parent
be83d2933c
commit
1472bd4f17
|
@ -84,10 +84,12 @@
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"lodash.omit": "^4.5.0",
|
"lodash.omit": "^4.5.0",
|
||||||
"lodash.samplesize": "^4.2.0",
|
"lodash.samplesize": "^4.2.0",
|
||||||
|
"lodash.set": "^4.3.2",
|
||||||
"lodash.shuffle": "^4.2.0",
|
"lodash.shuffle": "^4.2.0",
|
||||||
"lru_map": "^0.4.1",
|
"lru_map": "^0.4.1",
|
||||||
"mobx": "^6.6.1",
|
"mobx": "^6.6.1",
|
||||||
"mobx-react-lite": "^3.4.0",
|
"mobx-react-lite": "^3.4.0",
|
||||||
|
"mobx-utils": "^6.0.6",
|
||||||
"normalize-url": "^8.0.0",
|
"normalize-url": "^8.0.0",
|
||||||
"patch-package": "^6.5.1",
|
"patch-package": "^6.5.1",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
|
@ -143,6 +145,7 @@
|
||||||
"@types/lodash.isequal": "^4.5.6",
|
"@types/lodash.isequal": "^4.5.6",
|
||||||
"@types/lodash.omit": "^4.5.7",
|
"@types/lodash.omit": "^4.5.7",
|
||||||
"@types/lodash.samplesize": "^4.2.7",
|
"@types/lodash.samplesize": "^4.2.7",
|
||||||
|
"@types/lodash.set": "^4.3.7",
|
||||||
"@types/lodash.shuffle": "^4.2.7",
|
"@types/lodash.shuffle": "^4.2.7",
|
||||||
"@types/react-avatar-editor": "^13.0.0",
|
"@types/react-avatar-editor": "^13.0.0",
|
||||||
"@types/react-native": "^0.67.3",
|
"@types/react-native": "^0.67.3",
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import {runInAction} from 'mobx'
|
||||||
|
import {deepObserve} from 'mobx-utils'
|
||||||
|
import set from 'lodash.set'
|
||||||
|
|
||||||
|
const ongoingActions = new Set<any>()
|
||||||
|
|
||||||
|
export const updateDataOptimistically = async <
|
||||||
|
T extends Record<string, any>,
|
||||||
|
U,
|
||||||
|
>(
|
||||||
|
model: T,
|
||||||
|
preUpdate: () => void,
|
||||||
|
serverUpdate: () => Promise<U>,
|
||||||
|
postUpdate?: (res: U) => void,
|
||||||
|
): Promise<void> => {
|
||||||
|
if (ongoingActions.has(model)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ongoingActions.add(model)
|
||||||
|
|
||||||
|
const prevState: Map<string, any> = new Map<string, any>()
|
||||||
|
const dispose = deepObserve(model, (change, path) => {
|
||||||
|
if (change.observableKind === 'object') {
|
||||||
|
if (change.type === 'update') {
|
||||||
|
prevState.set(
|
||||||
|
[path, change.name].filter(Boolean).join('.'),
|
||||||
|
change.oldValue,
|
||||||
|
)
|
||||||
|
} else if (change.type === 'add') {
|
||||||
|
prevState.set([path, change.name].filter(Boolean).join('.'), undefined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
preUpdate()
|
||||||
|
dispose()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await serverUpdate()
|
||||||
|
runInAction(() => {
|
||||||
|
postUpdate?.(res)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
runInAction(() => {
|
||||||
|
prevState.forEach((value, path) => {
|
||||||
|
set(model, path, value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
throw error
|
||||||
|
} finally {
|
||||||
|
ongoingActions.delete(model)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import {AtUri} from '@atproto/api'
|
||||||
import {RootStoreModel} from '../root-store'
|
import {RootStoreModel} from '../root-store'
|
||||||
import * as apilib from 'lib/api/index'
|
import * as apilib from 'lib/api/index'
|
||||||
import {cleanError} from 'lib/strings/errors'
|
import {cleanError} from 'lib/strings/errors'
|
||||||
|
import {updateDataOptimistically} from 'lib/async/revertible'
|
||||||
|
|
||||||
function* reactKeyGenerator(): Generator<string> {
|
function* reactKeyGenerator(): Generator<string> {
|
||||||
let counter = 0
|
let counter = 0
|
||||||
|
@ -134,45 +135,56 @@ export class PostThreadItemModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleLike() {
|
async toggleLike() {
|
||||||
if (this.post.viewer?.like) {
|
|
||||||
await this.rootStore.agent.deleteLike(this.post.viewer.like)
|
|
||||||
runInAction(() => {
|
|
||||||
this.post.likeCount = this.post.likeCount || 0
|
|
||||||
this.post.viewer = this.post.viewer || {}
|
this.post.viewer = this.post.viewer || {}
|
||||||
this.post.likeCount--
|
if (this.post.viewer.like) {
|
||||||
this.post.viewer.like = undefined
|
const url = this.post.viewer.like
|
||||||
})
|
await updateDataOptimistically(
|
||||||
|
this.post,
|
||||||
|
() => {
|
||||||
|
this.post.likeCount = (this.post.likeCount || 0) - 1
|
||||||
|
this.post.viewer!.like = undefined
|
||||||
|
},
|
||||||
|
() => this.rootStore.agent.deleteLike(url),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
const res = await this.rootStore.agent.like(this.post.uri, this.post.cid)
|
await updateDataOptimistically(
|
||||||
runInAction(() => {
|
this.post,
|
||||||
this.post.likeCount = this.post.likeCount || 0
|
() => {
|
||||||
this.post.viewer = this.post.viewer || {}
|
this.post.likeCount = (this.post.likeCount || 0) + 1
|
||||||
this.post.likeCount++
|
this.post.viewer!.like = 'pending'
|
||||||
this.post.viewer.like = res.uri
|
},
|
||||||
})
|
() => this.rootStore.agent.like(this.post.uri, this.post.cid),
|
||||||
|
res => {
|
||||||
|
this.post.viewer!.like = res.uri
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleRepost() {
|
async toggleRepost() {
|
||||||
|
this.post.viewer = this.post.viewer || {}
|
||||||
if (this.post.viewer?.repost) {
|
if (this.post.viewer?.repost) {
|
||||||
await this.rootStore.agent.deleteRepost(this.post.viewer.repost)
|
const url = this.post.viewer.repost
|
||||||
runInAction(() => {
|
await updateDataOptimistically(
|
||||||
this.post.repostCount = this.post.repostCount || 0
|
this.post,
|
||||||
this.post.viewer = this.post.viewer || {}
|
() => {
|
||||||
this.post.repostCount--
|
this.post.repostCount = (this.post.repostCount || 0) - 1
|
||||||
this.post.viewer.repost = undefined
|
this.post.viewer!.repost = undefined
|
||||||
})
|
},
|
||||||
} else {
|
() => this.rootStore.agent.deleteRepost(url),
|
||||||
const res = await this.rootStore.agent.repost(
|
)
|
||||||
this.post.uri,
|
} else {
|
||||||
this.post.cid,
|
await updateDataOptimistically(
|
||||||
|
this.post,
|
||||||
|
() => {
|
||||||
|
this.post.repostCount = (this.post.repostCount || 0) + 1
|
||||||
|
this.post.viewer!.repost = 'pending'
|
||||||
|
},
|
||||||
|
() => this.rootStore.agent.repost(this.post.uri, this.post.cid),
|
||||||
|
res => {
|
||||||
|
this.post.viewer!.repost = res.uri
|
||||||
|
},
|
||||||
)
|
)
|
||||||
runInAction(() => {
|
|
||||||
this.post.repostCount = this.post.repostCount || 0
|
|
||||||
this.post.viewer = this.post.viewer || {}
|
|
||||||
this.post.repostCount++
|
|
||||||
this.post.viewer.repost = res.uri
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import {
|
||||||
mergePosts,
|
mergePosts,
|
||||||
} from 'lib/api/build-suggested-posts'
|
} from 'lib/api/build-suggested-posts'
|
||||||
import {FeedTuner, FeedViewPostsSlice} from 'lib/api/feed-manip'
|
import {FeedTuner, FeedViewPostsSlice} from 'lib/api/feed-manip'
|
||||||
|
import {updateDataOptimistically} from 'lib/async/revertible'
|
||||||
|
|
||||||
type FeedViewPost = AppBskyFeedDefs.FeedViewPost
|
type FeedViewPost = AppBskyFeedDefs.FeedViewPost
|
||||||
type ReasonRepost = AppBskyFeedDefs.ReasonRepost
|
type ReasonRepost = AppBskyFeedDefs.ReasonRepost
|
||||||
|
@ -91,45 +92,56 @@ export class PostsFeedItemModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleLike() {
|
async toggleLike() {
|
||||||
if (this.post.viewer?.like) {
|
|
||||||
await this.rootStore.agent.deleteLike(this.post.viewer.like)
|
|
||||||
runInAction(() => {
|
|
||||||
this.post.likeCount = this.post.likeCount || 0
|
|
||||||
this.post.viewer = this.post.viewer || {}
|
this.post.viewer = this.post.viewer || {}
|
||||||
this.post.likeCount--
|
if (this.post.viewer.like) {
|
||||||
this.post.viewer.like = undefined
|
const url = this.post.viewer.like
|
||||||
})
|
await updateDataOptimistically(
|
||||||
|
this.post,
|
||||||
|
() => {
|
||||||
|
this.post.likeCount = (this.post.likeCount || 0) - 1
|
||||||
|
this.post.viewer!.like = undefined
|
||||||
|
},
|
||||||
|
() => this.rootStore.agent.deleteLike(url),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
const res = await this.rootStore.agent.like(this.post.uri, this.post.cid)
|
await updateDataOptimistically(
|
||||||
runInAction(() => {
|
this.post,
|
||||||
this.post.likeCount = this.post.likeCount || 0
|
() => {
|
||||||
this.post.viewer = this.post.viewer || {}
|
this.post.likeCount = (this.post.likeCount || 0) + 1
|
||||||
this.post.likeCount++
|
this.post.viewer!.like = 'pending'
|
||||||
this.post.viewer.like = res.uri
|
},
|
||||||
})
|
() => this.rootStore.agent.like(this.post.uri, this.post.cid),
|
||||||
|
res => {
|
||||||
|
this.post.viewer!.like = res.uri
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleRepost() {
|
async toggleRepost() {
|
||||||
|
this.post.viewer = this.post.viewer || {}
|
||||||
if (this.post.viewer?.repost) {
|
if (this.post.viewer?.repost) {
|
||||||
await this.rootStore.agent.deleteRepost(this.post.viewer.repost)
|
const url = this.post.viewer.repost
|
||||||
runInAction(() => {
|
await updateDataOptimistically(
|
||||||
this.post.repostCount = this.post.repostCount || 0
|
this.post,
|
||||||
this.post.viewer = this.post.viewer || {}
|
() => {
|
||||||
this.post.repostCount--
|
this.post.repostCount = (this.post.repostCount || 0) - 1
|
||||||
this.post.viewer.repost = undefined
|
this.post.viewer!.repost = undefined
|
||||||
})
|
},
|
||||||
} else {
|
() => this.rootStore.agent.deleteRepost(url),
|
||||||
const res = await this.rootStore.agent.repost(
|
)
|
||||||
this.post.uri,
|
} else {
|
||||||
this.post.cid,
|
await updateDataOptimistically(
|
||||||
|
this.post,
|
||||||
|
() => {
|
||||||
|
this.post.repostCount = (this.post.repostCount || 0) + 1
|
||||||
|
this.post.viewer!.repost = 'pending'
|
||||||
|
},
|
||||||
|
() => this.rootStore.agent.repost(this.post.uri, this.post.cid),
|
||||||
|
res => {
|
||||||
|
this.post.viewer!.repost = res.uri
|
||||||
|
},
|
||||||
)
|
)
|
||||||
runInAction(() => {
|
|
||||||
this.post.repostCount = this.post.repostCount || 0
|
|
||||||
this.post.viewer = this.post.viewer || {}
|
|
||||||
this.post.repostCount++
|
|
||||||
this.post.viewer.repost = res.uri
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,8 +103,6 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
}),
|
}),
|
||||||
[theme],
|
[theme],
|
||||||
) as StyleProp<ViewStyle>
|
) as StyleProp<ViewStyle>
|
||||||
const [repostMod, setRepostMod] = React.useState<number>(0)
|
|
||||||
const [likeMod, setLikeMod] = React.useState<number>(0)
|
|
||||||
// DISABLED see #135
|
// DISABLED see #135
|
||||||
// const repostRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
// const repostRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
||||||
// const likeRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
// const likeRef = React.useRef<TriggerableAnimatedRef | null>(null)
|
||||||
|
@ -112,11 +110,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
store.shell.closeModal()
|
store.shell.closeModal()
|
||||||
if (!opts.isReposted) {
|
if (!opts.isReposted) {
|
||||||
ReactNativeHapticFeedback.trigger('impactMedium')
|
ReactNativeHapticFeedback.trigger('impactMedium')
|
||||||
setRepostMod(1)
|
opts.onPressToggleRepost().catch(_e => undefined)
|
||||||
opts
|
|
||||||
.onPressToggleRepost()
|
|
||||||
.catch(_e => undefined)
|
|
||||||
.then(() => setRepostMod(0))
|
|
||||||
// DISABLED see #135
|
// DISABLED see #135
|
||||||
// repostRef.current?.trigger(
|
// repostRef.current?.trigger(
|
||||||
// {start: ctrlAnimStart, style: ctrlAnimStyle},
|
// {start: ctrlAnimStart, style: ctrlAnimStyle},
|
||||||
|
@ -126,11 +120,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
// },
|
// },
|
||||||
// )
|
// )
|
||||||
} else {
|
} else {
|
||||||
setRepostMod(-1)
|
opts.onPressToggleRepost().catch(_e => undefined)
|
||||||
opts
|
|
||||||
.onPressToggleRepost()
|
|
||||||
.catch(_e => undefined)
|
|
||||||
.then(() => setRepostMod(0))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,14 +147,10 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPressToggleLikeWrapper = () => {
|
const onPressToggleLikeWrapper = async () => {
|
||||||
if (!opts.isLiked) {
|
if (!opts.isLiked) {
|
||||||
ReactNativeHapticFeedback.trigger('impactMedium')
|
ReactNativeHapticFeedback.trigger('impactMedium')
|
||||||
setLikeMod(1)
|
await opts.onPressToggleLike().catch(_e => undefined)
|
||||||
opts
|
|
||||||
.onPressToggleLike()
|
|
||||||
.catch(_e => undefined)
|
|
||||||
.then(() => setLikeMod(0))
|
|
||||||
// DISABLED see #135
|
// DISABLED see #135
|
||||||
// likeRef.current?.trigger(
|
// likeRef.current?.trigger(
|
||||||
// {start: ctrlAnimStart, style: ctrlAnimStyle},
|
// {start: ctrlAnimStart, style: ctrlAnimStyle},
|
||||||
|
@ -173,12 +159,10 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
// setLikeMod(0)
|
// setLikeMod(0)
|
||||||
// },
|
// },
|
||||||
// )
|
// )
|
||||||
|
// setIsLikedPressed(false)
|
||||||
} else {
|
} else {
|
||||||
setLikeMod(-1)
|
await opts.onPressToggleLike().catch(_e => undefined)
|
||||||
opts
|
// setIsLikedPressed(false)
|
||||||
.onPressToggleLike()
|
|
||||||
.catch(_e => undefined)
|
|
||||||
.then(() => setLikeMod(0))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,35 +194,22 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
style={styles.ctrl}>
|
style={styles.ctrl}>
|
||||||
<RepostIcon
|
<RepostIcon
|
||||||
style={
|
style={
|
||||||
opts.isReposted || repostMod > 0
|
opts.isReposted
|
||||||
? (styles.ctrlIconReposted as StyleProp<ViewStyle>)
|
? (styles.ctrlIconReposted as StyleProp<ViewStyle>)
|
||||||
: defaultCtrlColor
|
: defaultCtrlColor
|
||||||
}
|
}
|
||||||
strokeWidth={2.4}
|
strokeWidth={2.4}
|
||||||
size={opts.big ? 24 : 20}
|
size={opts.big ? 24 : 20}
|
||||||
/>
|
/>
|
||||||
{
|
|
||||||
undefined /*DISABLED see #135 <TriggerableAnimated ref={repostRef}>
|
|
||||||
<RepostIcon
|
|
||||||
style={
|
|
||||||
(opts.isReposted
|
|
||||||
? styles.ctrlIconReposted
|
|
||||||
: defaultCtrlColor) as ViewStyle
|
|
||||||
}
|
|
||||||
strokeWidth={2.4}
|
|
||||||
size={opts.big ? 24 : 20}
|
|
||||||
/>
|
|
||||||
</TriggerableAnimated>*/
|
|
||||||
}
|
|
||||||
{typeof opts.repostCount !== 'undefined' ? (
|
{typeof opts.repostCount !== 'undefined' ? (
|
||||||
<Text
|
<Text
|
||||||
testID="repostCount"
|
testID="repostCount"
|
||||||
style={
|
style={
|
||||||
opts.isReposted || repostMod > 0
|
opts.isReposted
|
||||||
? [s.bold, s.green3, s.f15, s.ml5]
|
? [s.bold, s.green3, s.f15, s.ml5]
|
||||||
: [defaultCtrlColor, s.f15, s.ml5]
|
: [defaultCtrlColor, s.f15, s.ml5]
|
||||||
}>
|
}>
|
||||||
{opts.repostCount + repostMod}
|
{opts.repostCount}
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
@ -249,7 +220,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
style={styles.ctrl}
|
style={styles.ctrl}
|
||||||
hitSlop={HITSLOP}
|
hitSlop={HITSLOP}
|
||||||
onPress={onPressToggleLikeWrapper}>
|
onPress={onPressToggleLikeWrapper}>
|
||||||
{opts.isLiked || likeMod > 0 ? (
|
{opts.isLiked ? (
|
||||||
<HeartIconSolid
|
<HeartIconSolid
|
||||||
style={styles.ctrlIconLiked as StyleProp<ViewStyle>}
|
style={styles.ctrlIconLiked as StyleProp<ViewStyle>}
|
||||||
size={opts.big ? 22 : 16}
|
size={opts.big ? 22 : 16}
|
||||||
|
@ -261,34 +232,15 @@ export function PostCtrls(opts: PostCtrlsOpts) {
|
||||||
size={opts.big ? 20 : 16}
|
size={opts.big ? 20 : 16}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{
|
|
||||||
undefined /*DISABLED see #135 <TriggerableAnimated ref={likeRef}>
|
|
||||||
{opts.isLiked || likeMod > 0 ? (
|
|
||||||
<HeartIconSolid
|
|
||||||
style={styles.ctrlIconLiked as ViewStyle}
|
|
||||||
size={opts.big ? 22 : 16}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<HeartIcon
|
|
||||||
style={[
|
|
||||||
defaultCtrlColor as ViewStyle,
|
|
||||||
opts.big ? styles.mt1 : undefined,
|
|
||||||
]}
|
|
||||||
strokeWidth={3}
|
|
||||||
size={opts.big ? 20 : 16}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</TriggerableAnimated>*/
|
|
||||||
}
|
|
||||||
{typeof opts.likeCount !== 'undefined' ? (
|
{typeof opts.likeCount !== 'undefined' ? (
|
||||||
<Text
|
<Text
|
||||||
testID="likeCount"
|
testID="likeCount"
|
||||||
style={
|
style={
|
||||||
opts.isLiked || likeMod > 0
|
opts.isLiked
|
||||||
? [s.bold, s.red3, s.f15, s.ml5]
|
? [s.bold, s.red3, s.f15, s.ml5]
|
||||||
: [defaultCtrlColor, s.f15, s.ml5]
|
: [defaultCtrlColor, s.f15, s.ml5]
|
||||||
}>
|
}>
|
||||||
{opts.likeCount + likeMod}
|
{opts.likeCount}
|
||||||
</Text>
|
</Text>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
17
yarn.lock
17
yarn.lock
|
@ -4540,6 +4540,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/lodash" "*"
|
"@types/lodash" "*"
|
||||||
|
|
||||||
|
"@types/lodash.set@^4.3.7":
|
||||||
|
version "4.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash.set/-/lodash.set-4.3.7.tgz#784fccea3fbef4d0949d1897a780f592da700942"
|
||||||
|
integrity sha512-bS5Wkg/nrT82YUfkNYPSccFrNZRL+irl7Yt4iM6OTSQ0VZJED2oUIVm15NkNtUAQ8SRhCe+axqERUV6MJgkeEg==
|
||||||
|
dependencies:
|
||||||
|
"@types/lodash" "*"
|
||||||
|
|
||||||
"@types/lodash.shuffle@^4.2.7":
|
"@types/lodash.shuffle@^4.2.7":
|
||||||
version "4.2.7"
|
version "4.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash.shuffle/-/lodash.shuffle-4.2.7.tgz#b714d829af948a266b0df1477d629c70de2f4c72"
|
resolved "https://registry.yarnpkg.com/@types/lodash.shuffle/-/lodash.shuffle-4.2.7.tgz#b714d829af948a266b0df1477d629c70de2f4c72"
|
||||||
|
@ -11767,6 +11774,11 @@ lodash.samplesize@^4.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.samplesize/-/lodash.samplesize-4.2.0.tgz#460762fbb2b342290517499e90d51586db465ff9"
|
resolved "https://registry.yarnpkg.com/lodash.samplesize/-/lodash.samplesize-4.2.0.tgz#460762fbb2b342290517499e90d51586db465ff9"
|
||||||
integrity sha512-1ZhKV7/nuISuaQdxfCqrs4HHxXIYN+0Z4f7NMQn2PHkxFZJGavJQ1j/paxyJnLJmN2ZamNN6SMepneV+dCgQTA==
|
integrity sha512-1ZhKV7/nuISuaQdxfCqrs4HHxXIYN+0Z4f7NMQn2PHkxFZJGavJQ1j/paxyJnLJmN2ZamNN6SMepneV+dCgQTA==
|
||||||
|
|
||||||
|
lodash.set@^4.3.2:
|
||||||
|
version "4.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
|
||||||
|
integrity sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==
|
||||||
|
|
||||||
lodash.shuffle@^4.2.0:
|
lodash.shuffle@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.shuffle/-/lodash.shuffle-4.2.0.tgz#145b5053cf875f6f5c2a33f48b6e9948c6ec7b4b"
|
resolved "https://registry.yarnpkg.com/lodash.shuffle/-/lodash.shuffle-4.2.0.tgz#145b5053cf875f6f5c2a33f48b6e9948c6ec7b4b"
|
||||||
|
@ -12514,6 +12526,11 @@ mobx-react-lite@^3.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz#3a4c22c30bfaa8b1b2aa48d12b2ba811c0947ab7"
|
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz#3a4c22c30bfaa8b1b2aa48d12b2ba811c0947ab7"
|
||||||
integrity sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==
|
integrity sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==
|
||||||
|
|
||||||
|
mobx-utils@^6.0.6:
|
||||||
|
version "6.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/mobx-utils/-/mobx-utils-6.0.6.tgz#99a2e0d54e958e4c9de4b35729e0c3768b6afc43"
|
||||||
|
integrity sha512-lzJtxOWgj3Dp2HeXviInV3ZRY4YhThzRHXuy90oKXDH2g+ymJGIts4bdjb7NQuSi34V25cMZoQX7TkHJQuKLOQ==
|
||||||
|
|
||||||
mobx@^6.6.1:
|
mobx@^6.6.1:
|
||||||
version "6.8.0"
|
version "6.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.8.0.tgz#59051755fdb5c8a9f3f2e0a9b6abaf86bab7f843"
|
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.8.0.tgz#59051755fdb5c8a9f3f2e0a9b6abaf86bab7f843"
|
||||||
|
|
Loading…
Reference in New Issue