composables.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import {
  2. customRef,
  3. onBeforeUnmount,
  4. readonly,
  5. Ref,
  6. ref,
  7. shallowRef,
  8. useContext,
  9. watch,
  10. wrapProperty,
  11. } from "@nuxtjs/composition-api"
  12. import { Observable, Subscription } from "rxjs"
  13. export const useNuxt = wrapProperty("$nuxt")
  14. export function useReadonlyStream<T>(
  15. stream$: Observable<T>,
  16. initialValue: T
  17. ): Ref<T> {
  18. let sub: Subscription | null = null
  19. onBeforeUnmount(() => {
  20. if (sub) {
  21. sub.unsubscribe()
  22. }
  23. })
  24. const targetRef = ref(initialValue) as Ref<T>
  25. sub = stream$.subscribe((value) => {
  26. targetRef.value = value
  27. })
  28. return readonly(targetRef) as Ref<T>
  29. }
  30. export function useStream<T>(
  31. stream$: Observable<T>,
  32. initialValue: T,
  33. setter: (val: T) => void
  34. ) {
  35. let sub: Subscription | null = null
  36. onBeforeUnmount(() => {
  37. if (sub) {
  38. sub.unsubscribe()
  39. }
  40. })
  41. return customRef((track, trigger) => {
  42. let value = initialValue
  43. sub = stream$.subscribe((val) => {
  44. value = val
  45. trigger()
  46. })
  47. return {
  48. get() {
  49. track()
  50. return value
  51. },
  52. set(value: T) {
  53. trigger()
  54. setter(value)
  55. },
  56. }
  57. })
  58. }
  59. export function pluckRef<T, K extends keyof T>(ref: Ref<T>, key: K): Ref<T[K]> {
  60. return customRef((track, trigger) => {
  61. const stopWatching = watch(ref, (newVal, oldVal) => {
  62. if (newVal[key] !== oldVal[key]) {
  63. trigger()
  64. }
  65. })
  66. onBeforeUnmount(() => {
  67. stopWatching()
  68. })
  69. return {
  70. get() {
  71. track()
  72. return ref.value[key]
  73. },
  74. set(value: T[K]) {
  75. trigger()
  76. ref.value = Object.assign(ref.value, { [key]: value })
  77. },
  78. }
  79. })
  80. }
  81. export function pluckMultipleFromRef<T, K extends Array<keyof T>>(
  82. sourceRef: Ref<T>,
  83. keys: K
  84. ): { [key in K[number]]: Ref<T[key]> } {
  85. return Object.fromEntries(keys.map((x) => [x, pluckRef(sourceRef, x)])) as any
  86. }
  87. export type StreamSubscriberFunc = <T>(
  88. stream: Observable<T>,
  89. next?: ((value: T) => void) | undefined,
  90. error?: ((e: any) => void) | undefined,
  91. complete?: (() => void) | undefined
  92. ) => void
  93. /**
  94. * A composable that provides the ability to run streams
  95. * and subscribe to them and respect the component lifecycle.
  96. */
  97. export function useStreamSubscriber(): {
  98. subscribeToStream: StreamSubscriberFunc
  99. } {
  100. const subs: Subscription[] = []
  101. const runAndSubscribe = <T>(
  102. stream: Observable<T>,
  103. next?: (value: T) => void,
  104. error?: (e: any) => void,
  105. complete?: () => void
  106. ) => {
  107. const sub = stream.subscribe({
  108. next,
  109. error,
  110. complete: () => {
  111. if (complete) complete()
  112. subs.splice(subs.indexOf(sub), 1)
  113. },
  114. })
  115. subs.push(sub)
  116. }
  117. onBeforeUnmount(() => {
  118. subs.forEach((sub) => sub.unsubscribe())
  119. })
  120. return {
  121. subscribeToStream: runAndSubscribe,
  122. }
  123. }
  124. export function useI18nPathInfo() {
  125. const { localePath, getRouteBaseName } = useContext() as any
  126. return {
  127. localePath: localePath as (x: string) => string,
  128. getRouteBaseName: getRouteBaseName as (x?: any) => string, // Should be a route
  129. }
  130. }
  131. export function useI18n() {
  132. const {
  133. app: { i18n },
  134. } = useContext()
  135. return i18n.t.bind(i18n)
  136. }
  137. export function useToast() {
  138. const { $toast } = useContext()
  139. return $toast
  140. }
  141. export function useColorMode() {
  142. const { $colorMode } = useContext()
  143. return $colorMode
  144. }
  145. export function useAxios() {
  146. const { $axios } = useContext()
  147. return $axios
  148. }
  149. export function usePolled<T>(
  150. pollDurationMS: number,
  151. pollFunc: (stopPolling: () => void) => T
  152. ): Ref<T> {
  153. let polling = true
  154. let handle: ReturnType<typeof setInterval> | undefined
  155. const stopPolling = () => {
  156. if (handle) {
  157. clearInterval(handle)
  158. handle = undefined
  159. polling = false
  160. }
  161. }
  162. const result = shallowRef(pollFunc(stopPolling))
  163. if (polling) {
  164. handle = setInterval(() => {
  165. result.value = pollFunc(stopPolling)
  166. }, pollDurationMS)
  167. }
  168. onBeforeUnmount(() => {
  169. if (polling) stopPolling()
  170. })
  171. return result
  172. }