import { 
 | 
  useInfiniteQuery, 
 | 
  QueryKey, 
 | 
  useMutation, 
 | 
  useQueryClient, 
 | 
  InfiniteData, 
 | 
  RefetchOptions, 
 | 
  RefetchQueryFilters, 
 | 
} from '@tanstack/vue-query'; 
 | 
import { UnwrapNestedRefs, Ref, ref, reactive, computed } from 'vue'; 
 | 
import Taro from '@tarojs/taro'; 
 | 
  
 | 
export type BaseData<T = any> = { 
 | 
  data?: T[]; 
 | 
  objectData?: any; 
 | 
  pageModel?: { 
 | 
    rows?: number; 
 | 
    page?: number; 
 | 
    totalCount?: number; 
 | 
    totalPage?: number; 
 | 
  }; 
 | 
}; 
 | 
  
 | 
export interface OrderInput { 
 | 
  property?: string; 
 | 
  order?: number; 
 | 
} 
 | 
  
 | 
export type ExtraParams = { 
 | 
  [key: string]: any; 
 | 
  orderByProperty?: string; 
 | 
}; 
 | 
  
 | 
export type ServiceContext = { 
 | 
  pageParam?: any; 
 | 
  signal?: AbortSignal; 
 | 
}; 
 | 
  
 | 
export type InfiniteGroupOptions = { 
 | 
  groupIndex?: number; 
 | 
  itemIndex?: number; 
 | 
}; 
 | 
  
 | 
type Service<TData, TExtraParams extends ExtraParams> = ( 
 | 
  context: ServiceContext, 
 | 
  extraParamsState: UnwrapNestedRefs<TExtraParams> 
 | 
) => Promise<TData>; 
 | 
  
 | 
type UseInfiniteLoadingOptions<T, TExtraParams extends ExtraParams> = { 
 | 
  /** 
 | 
   * @deprecated 
 | 
   */ 
 | 
  defaultExtraParams?: TExtraParams; 
 | 
  enabled?: Ref<boolean> | boolean; 
 | 
  queryKey?: QueryKey; 
 | 
  onSuccess?: (data: InfiniteData<BaseData<T>>) => void; 
 | 
  onRefetch?: () => any; 
 | 
  useLocalData?: boolean; 
 | 
  refeshDidShow?: boolean; 
 | 
  select?: (data: InfiniteData<BaseData<T>>) => InfiniteData<BaseData<T>>; 
 | 
}; 
 | 
// QueryFunction<BaseData<T>, (string | DeepUnwrapRef<UnwrapNestedRefs<TExtraParams>>)[], any> 
 | 
export function useInfiniteLoading<T, TExtraParams extends ExtraParams>( 
 | 
  service: Service<BaseData<T>, TExtraParams>, 
 | 
  options: UseInfiniteLoadingOptions<T, TExtraParams> = {} 
 | 
) { 
 | 
  const { 
 | 
    defaultExtraParams = {} as TExtraParams, 
 | 
    enabled = ref(true), 
 | 
    queryKey, 
 | 
    onSuccess, 
 | 
    onRefetch: _onRefetch, 
 | 
    useLocalData = false, 
 | 
    refeshDidShow = true, 
 | 
    select, 
 | 
  } = options; 
 | 
  
 | 
  const extraParamState = reactive({ 
 | 
    ...defaultExtraParams, 
 | 
  }); 
 | 
  
 | 
  const localData = ref({ 
 | 
    pageParams: [], 
 | 
    pages: [], 
 | 
  }); 
 | 
  
 | 
  const { 
 | 
    data, 
 | 
    error, 
 | 
    fetchNextPage, 
 | 
    hasNextPage, 
 | 
    isFetching, 
 | 
    isFetchingNextPage, 
 | 
    isLoading, 
 | 
    isError, 
 | 
    refetch, 
 | 
    isInitialLoading, 
 | 
    fetchPreviousPage, 
 | 
    hasPreviousPage, 
 | 
  } = useInfiniteQuery({ 
 | 
    queryKey: queryKey, 
 | 
    queryFn: ({ pageParam = 1, signal }) => { 
 | 
      return service({ pageParam, signal }, extraParamState); 
 | 
    }, 
 | 
    // initialPageParam: 1, 
 | 
    getNextPageParam: (lastPage, pages) => { 
 | 
      if (!lastPage) return 1; 
 | 
      if ( 
 | 
        (lastPage.pageModel.page - 1) * lastPage.pageModel.rows + lastPage.data.length < 
 | 
          lastPage.pageModel.totalCount && 
 | 
        lastPage.pageModel.totalCount > 0 
 | 
      ) { 
 | 
        return lastPage.pageModel.page + 1; 
 | 
      } 
 | 
    }, 
 | 
    getPreviousPageParam: (firstPage, pages) => { 
 | 
      if (!firstPage) return 1; 
 | 
      if ( 
 | 
        (firstPage.pageModel.page - 1) * firstPage.pageModel.rows + firstPage.data.length < 
 | 
          firstPage.pageModel.totalCount && 
 | 
        firstPage.pageModel.totalCount > 0 
 | 
      ) { 
 | 
        return firstPage.pageModel.page + 1; 
 | 
      } 
 | 
    }, 
 | 
    enabled: enabled, 
 | 
    onSuccess(data) { 
 | 
      // console.log('data2: ', data); 
 | 
      localData.value.pageParams = data.pageParams; 
 | 
      //@ts-ignore 
 | 
      localData.value.pages = data.pages; 
 | 
      onSuccess?.(data); 
 | 
    }, 
 | 
    select(data) { 
 | 
      return select ? select(data) : data; 
 | 
    }, 
 | 
  }); 
 | 
  
 | 
  const _data = computed<InfiniteData<BaseData<T>>>(() => 
 | 
    useLocalData ? (localData.value as any) : data.value 
 | 
  ); 
 | 
  
 | 
  const queryClient = useQueryClient(); 
 | 
  
 | 
  const { mutateAsync } = useMutation({ 
 | 
    mutationFn: ({ pageParam }: ServiceContext) => fetchNextPage(pageParam), 
 | 
  
 | 
    // onSettled: async () => { 
 | 
    //   return await queryClient.invalidateQueries({ queryKey: queryKey }); 
 | 
    // }, 
 | 
  }); 
 | 
  
 | 
  const { mutate: setListItem } = useMutation({ 
 | 
    mutationFn: (data: { dataKey?: string; data: Partial<T> }) => { 
 | 
      return Promise.resolve(data); 
 | 
    }, 
 | 
    onSuccess: ({ dataKey = 'id', data }) => { 
 | 
      console.log('dataKey: ', data); 
 | 
      const pagesArray: InfiniteData<BaseData<T>> = useLocalData 
 | 
        ? localData.value 
 | 
        : queryClient.getQueryData(queryKey); 
 | 
      console.log('pagesArray: ', pagesArray); 
 | 
      const newPagesArray = 
 | 
        pagesArray?.pages.map((page) => { 
 | 
          return { 
 | 
            ...page, 
 | 
            data: page.data.map((item) => { 
 | 
              if (item[dataKey] === data[dataKey]) { 
 | 
                return { 
 | 
                  ...item, 
 | 
                  ...data, 
 | 
                }; 
 | 
              } else { 
 | 
                return item; 
 | 
              } 
 | 
            }), 
 | 
          }; 
 | 
        }) ?? []; 
 | 
      if (useLocalData) { 
 | 
        console.log('newPagesArray: ', newPagesArray); 
 | 
        localData.value.pageParams = pagesArray.pageParams; 
 | 
        localData.value.pages = newPagesArray; 
 | 
      } else { 
 | 
        queryClient.setQueryData(queryKey, () => ({ 
 | 
          pages: newPagesArray, 
 | 
          pageParams: pagesArray.pageParams, 
 | 
        })); 
 | 
      } 
 | 
    }, 
 | 
  }); 
 | 
  
 | 
  function onRefetch(options?: RefetchOptions & RefetchQueryFilters<unknown>) { 
 | 
    _onRefetch?.(); 
 | 
    return refetch(options); 
 | 
  } 
 | 
  
 | 
  const infiniteLoadingProps = computed(() => ({ 
 | 
    fetchNextPage, 
 | 
    listData: _data.value, 
 | 
    flattenListData: flattenListData.value, 
 | 
    // error: error.value, 
 | 
    hasMore: hasNextPage.value, 
 | 
    isFetching: isFetching.value, 
 | 
    isFetchingNextPage: isFetchingNextPage.value, 
 | 
    isLoading: isLoading.value, 
 | 
    isError: !!isError.value, 
 | 
    refetch: onRefetch, 
 | 
  })); 
 | 
  
 | 
  const flattenListData = computed(() => { 
 | 
    let list: BaseData<T>['data'] = []; 
 | 
    if (data && _data.value) { 
 | 
      _data.value?.pages.forEach((group) => { 
 | 
        group.data.forEach((item) => { 
 | 
          list.push(item); 
 | 
        }); 
 | 
      }); 
 | 
    } 
 | 
    return list; 
 | 
  }); 
 | 
  
 | 
  if (refeshDidShow) { 
 | 
    useRefeshDidShow({ queryKey: queryKey }); 
 | 
  } 
 | 
  
 | 
  const infiniteLoadingRef = ref<{ backToTop(): void; scrollToBottom(dis?: number): void }>(); 
 | 
  
 | 
  function setByIndex(options: InfiniteGroupOptions = {}) { 
 | 
    refetch({ refetchPage: (page, index) => index === options.groupIndex, type: 'inactive' }); 
 | 
  } 
 | 
  
 | 
  function updatePageByIndex(options: InfiniteGroupOptions = {}) { 
 | 
    refetch({ refetchPage: (page, index) => index === options.groupIndex, type: 'inactive' }); 
 | 
  } 
 | 
  
 | 
  function remove() { 
 | 
    queryClient.invalidateQueries({ queryKey: queryKey }); 
 | 
  } 
 | 
  
 | 
  function invalidateQueries() { 
 | 
    return queryClient.invalidateQueries({ queryKey: queryKey }); 
 | 
  } 
 | 
  
 | 
  const listActions = { 
 | 
    setByIndex, 
 | 
    updatePageByIndex, 
 | 
    remove, 
 | 
  }; 
 | 
  
 | 
  return { 
 | 
    infiniteLoadingProps, 
 | 
    flattenListData, 
 | 
    extraParamState, 
 | 
    mutateAsync, 
 | 
    setListItem, 
 | 
    cancel: () => queryClient.cancelQueries(queryKey), 
 | 
    infiniteLoadingRef, 
 | 
    listActions, 
 | 
    invalidateQueries, 
 | 
    fetchPreviousPage, 
 | 
    hasPreviousPage, 
 | 
  }; 
 | 
} 
 | 
  
 | 
export type ListActionsType = { 
 | 
  setByIndex: (options?: InfiniteGroupOptions) => void; 
 | 
  updatePageByIndex: (options?: InfiniteGroupOptions) => void; 
 | 
  remove: () => void; 
 | 
}; 
 | 
  
 | 
type UseRefeshDidShowOptions = { 
 | 
  queryKey: QueryKey; 
 | 
}; 
 | 
  
 | 
export function useRefeshDidShow({ queryKey }: UseRefeshDidShowOptions) { 
 | 
  const queryClient = useQueryClient(); 
 | 
  const showUpdate = ref(false); 
 | 
  
 | 
  Taro.useDidShow(() => { 
 | 
    if (showUpdate.value) { 
 | 
      queryClient.invalidateQueries({ 
 | 
        queryKey: queryKey, 
 | 
      }); 
 | 
    } 
 | 
  }); 
 | 
  
 | 
  Taro.useDidHide(() => { 
 | 
    showUpdate.value = true; 
 | 
  }); 
 | 
  
 | 
  Taro.useUnload(() => { 
 | 
    showUpdate.value = false; 
 | 
  }); 
 | 
} 
 |