@appello/common v3.4.4
Common frontend library of components for web and mobile environmental
How to use it
Install package from npm:
npm i @appello/common
Import modules you need in your code:
import { useInterval, isNill } from '@appello/common';
Development guide
For developers
Each new functionality must be added to the folder and exported from the root! This is necessary to simplify the import of the necessary functionality:
import { useInterval, isFunction, ... } from '@appello/common';
If you need to create a new module, remember to add index.ts with exports.
Hooks
useCodeTimer
import { useCodeTimer } from '@appello/common';
Ready-made hook for setting a timer when sending SMS again.
API:
import { useCodeTimer } from "@appello/common";
const Component = () => {
  const { seconds, inProgress, sendCode } = useCodeTimer();
  
  return (
    <div>
      ...
      {
        inProgress && (
          <span>{seconds}</span>
        )
      }
      <button type="button" onClick={sendCode} />
      ...
    </div>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| defaultSeconds | Countdown time in seconds. Optional. | number | 59 | 
Return:
| Property | Description | Type | 
|---|---|---|
| seconds | Amount of time in seconds. | number | 
| inProgress | Syntactic sugar. Is the timer running? | boolean | 
| sendCode | Method that starts the timer. | void | 
useCombinedRef
import { useCombinedRef } from '@appello/common';
Hook for combining refs.
API:
import { useCombinedRef } from '@appello/common';
const Component = () => {
  const firstRef = useRef<number>(0);
  const secondRef = useRef<number>(0);
  const combinedRef = useCombinedRef(firstRef, secondRef);
  combinedRef(5);
}Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| listOfRefs | List of refs. | Ref | One of required | 
Return:
| Property | Description | Type | 
|---|---|---|
| combinedRef | Combined ref. | Ref | 
useStep
import { useStep } from '@appello/common';
React state hook that tracks a numeric value.
API:
import { useStep } from '@appello/common';
const Component = () => {
  const { count, setCount, increment, decrement, reset } = useStep(0)
  return (
    <div>
      <p>Count is {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
      <button onClick={reset}>Reset</button>
      <button onClick={() => setCount((x) => x * 2)}>Multiply by 2</button>
    </div>
  )
}Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| initialValue | The initial value for the counter. | number | 0 | 
Return:
| Property | Description | Type | 
|---|---|---|
| count | Current count. | number | 
| increment | Increment count. | () => void | 
| decrement | Decrement count. | () => void | 
| reset | Reset count. | () => void | 
| setCount | Set count. | (count: number) => void | 
useDebounceCallback
import { useDebounceCallback } from '@appello/common';
A hook that deal with the debounced function.
API:
import { useDebounceCallback } from '@appello/common';
const Component = () => {
  const [value, setValue] = useState(0);
  const { debounce } = useDebounceCallback(
    () => {
      setValue(value + 1);
    },
  );
  return (
    <button type="button" onClick={debounce}>
      Click
    </button>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| fn | Callback for the debounce. | AnyFuction | Required | 
| options | Lodash options | DebounceOptions | Optional | 
DebounceOptions:
export interface DebounceOptions {
  wait?: number;
  leading?: boolean;
  trailing?: boolean;
  maxWait?: number;
}Return:
| Property | Description | Type | 
|---|---|---|
| debounce | Invoke and pass parameters to fn. | AnyFuction | 
| cancel | Cancel the invocation of currently debounced function. | () => void | 
| flush | Immediately invoke currently debounced function. | () => void | 
useDebounceEffect
import { useDebounceEffect } from '@appello/common';
An effect with debouncing
API:
import { useDebounceEffect } from '@appello/common';
const Component = () => {
  const [value, setValue] = useState('hello');
  const [records, setRecords] = useState<string[]>([]);
  useDebounceEffect(
    () => {
      setRecords((val) => [...val, value]);
    },
    [value],
  );
  return (
    <>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Write text"
      />
      <ul>
        {records.map((record, index) => (
          <li key={index}>{record}</li>
        ))}
      </ul>
    </>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| effect | The effect callback. | EffectCallback | Required | 
| deps | The dependencies list. | DependencyList | undefined | 
| options | Config for the debounce behaviors. See the Options section below for details. | DebounceOptions | undefined | 
DebounceOptions:
export interface DebounceOptions {
  wait?: number;
  leading?: boolean;
  trailing?: boolean;
  maxWait?: number;
}useDefault
import { useDefault } from '@appello/common';
React state hook that returns the default value when state is null or undefined.
API:
import { useDefault } from '@appello/common';
const Component = () => {
  const [user, setUser] = useDefault({ name: 'Mathers' }, { name: 'Marshall' });
  
  return (
    <div>
      <div>User: {user.name}</div>
      <input onChange={e => setUser({ name: e.target.value })} />
      <button onClick={() => setUser(null)}>Set to null</button>
    </div>
  )
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| defaultValue | Default value. | any | Required | 
| initialValue | Initial value. | any | Required | 
Return:
| Property | Description | Type | 
|---|---|---|
| state | State and set state. | [any, Dispatch<SetStateAction<any>>] | 
useFirstMountState
import { useFirstMountState } from '@appello/common';
A hook that returns the first rendering
API:
import { useFirstMountState } from '@appello/common';
const Component = () => {
  const isFirstMount = useFirstMountState();
  return (
    <div>
      {isFirstMount}
    </div>
  )
};Return:
| Property | Description | Type | 
|---|---|---|
| isFirstMount | Return first render | bollean | 
useHookFormPersist
import { useHookFormPersist } from '@appello/common';
A hook for save temporary data from React hook form inside storage (LocalStorage / SessionStorage / AsyncStorage).
API:
import { useHookFormPersist } from "@appello/common";
import { Controller } from 'react-hook-form';
import AsyncStorage from '@react-native-async-storage/async-storage';
type FormType = {
  name: string;
  email: string;
}
const Component = () => {
  const { syncFromStorage, syncToStorage } = useHookFormPersist<FormType>({
    key: 'StorageKey',
    storage: AsyncStorage,
  });
  const {
    control,
    submit,
    watch,
    formState: { defaultValues },
  } = useForm<FormType>({
    defaultValues: () => {
      // Sync data form storage (you can select sync-policy)
      return syncFromStorage({
        defaultValues: {
          name: '',
          email: ''
        },
      });
    },
  })
  // Sync to storage
  syncToStorage(watch());
  return (
    <div>
      <Controller
        control={control}
        name="name"
        render={({ field: { value, onChange } }) => (
          <input
            type="text"
            value={value}
            onChange={onChange}
          />
        )}
      />
    </div>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| key | Key for storage | string | Required | 
| storage | Object Storage | Storage | Required | 
| excludeFields | Fields that should not be saved and synchronized | string[] | Optional | 
| includeFields | fields that should be saved and synchronized | string[] | Optional | 
Return:
| Property | Description | Type | 
|---|---|---|
| syncFromStorage | Sync values from storage. | ({ defaultValues?: UseFormProps<FieldValues>['defaultValues']; syncPolicy?: CachePolicyLiteral; }) => Promise | 
| syncToStorage | Sync values to storage. | (watchedValues: FieldValues) => void | 
| clear | Clear storage by key. | () => void | 
useInterval
import { useInterval } from '@appello/common';
A hook that allows you to call a callback function in a time interval
API:
import { useInterval } from '@appello/common';
const Component = () => {
  const { start } = useInterval({
    fn: () => {
      console.log('Hello')
    },
  });
  return (
    <button type="button" onClick={start}>
      Start timer
    </button>
  )
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| fn | Callback function, a function that will be called with a certain delay. | EffectCallback | Required | 
| delay | Delay time. | number | 1000 ms | 
| immediateStart | Starts immediately. | bollean | undefined | 
| immediateCallFn | Is it necessary to call a callback immediately before the timer starts. | bollean | undefined | 
| onStop | A callback that will be called after the timer stops. | () => void | undefined | 
Return:
| Property | Description | Type | 
|---|---|---|
| start | Function for starting timer. | () => void | 
| stop | Function for stopping timer. | () => void | 
| setState | Method for change state of an interval manually. | (v: bollean) => void | 
useIsClient
import { useIsClient } from '@appello/common';
This hook can be useful in an SSR environment to wait until be in a browser to execution some functions.
API:
import { useIsClient } from '@appello/common';
const Component = () => {
  const isClient = useIsClient()
  return <div>{isClient ? 'Client' : 'server'}</div>
};Return:
| Property | Description | Type | 
|---|---|---|
| value | Value is boolean | boolean | 
useIsMounted
import { useIsMounted } from '@appello/common';
This hook can be useful for checking is mount component.
API:
import { useIsMounted } from '@appello/common';
const Component = () => {
  const isMounted = useIsMounted()
  return <div>{isMounted() && 'Component is mount'}</div>
};Return:
| Property | Description | Type | 
|---|---|---|
| value | Value is boolean | boolean | 
useLatest
import { useLatest } from '@appello/common';
A Hook that returns the latest value, effectively avoiding the closure problem.
API:
import { useLatest } from "@appello/common";
const Component = () => {
  const [count, setCount] = useState(0);
  const latestCountRef = useLatest(count);
  useEffect(() => {
    const interval = setInterval(() => {
      setCount(latestCountRef.current + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);
  return (
    <>
      <p>count: {count}</p>
    </>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| value | Function, ref or any type value for get last active value. | any | Required | 
Return:
| Property | Description | Type | 
|---|---|---|
| resultValue | Latest value. | any | 
useManualUpdate
import { useManualUpdate } from '@appello/common';
A hook that can be used to manually update a component.
API:
import { useManualUpdate } from '@appello/common';
const Component = () => {
  const update = useManualUpdate();
  return (
    <>
      <div>Time: {Date.now()}</div>
      <button type="button" onClick={update}>
        Update
      </button>
    </>
  );
};Return:
| Property | Description | Type | 
|---|---|---|
| update | Callback for update component. | () => void | 
useMemoCallback
import { useMemoCallback } from '@appello/common';
Hooks for persistent functions. In theory, useMemoizedFn can be used instead of useCallback.
In some scenarios, we need to use useCallback to cache a function, but when the second parameter deps changes, the function will be regenerated, causing the function reference to change.
API:
import { useMemoCallback } from '@appello/common';
const Component = () => {
  const [count, setCount] = useState(0);
  const memoCallback = useMemoCallback(() => {
    message.info(`Current count is ${count}`);
  });
  return (
    <>
      <p>count: {count}</p>
      <button
        type="button"
        onClick={() => setCount((v) => v + 1)}
      >
        Add Count
      </button>
      <button type="button" onClick={memoCallback}>
        Call
      </button>
    </>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| callback | Callback for memorize. | AnyFunction | Required | 
Return:
| Property | Description | Type | 
|---|---|---|
| callback | Function that require persistence. | AnyFunction | 
useMountEffect
import { useMountEffect } from '@appello/common';
A hook that executes a function after the component is mounted.
API:
import { useMountEffect } from '@appello/common';
const Component = () => {
  useMountEffect(() => {
    console.log("Mount")
  })
  return (
    <span>component</span>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| callback | Callback that is called when the component is mounted. | AnyFunction | Required | 
usePrevious
import { usePrevious } from '@appello/common';
A Hook to return to the previous state.
API:
import { usePrevious } from '@appello/common';
const Component = () => {
  const [count, setCount] = useState(0);
  const previous = usePrevious(count);
  
  return (
    <>
      <div>counter current value: {count}</div>
      <div>counter previous value: {previous}</div>
      <button type="button" onClick={() => setCount((c) => c + 1)}>
        increase
      </button>
      <button type="button" onClick={() => setCount((c) => c - 1)}>
        decrease
      </button>
    </>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| value | Any value that will be saved. | any | Required | 
Return:
| Property | Description | Type | 
|---|---|---|
| value | Any value that saved. | any | 
useQueryParamsBuilder
import { useQueryParamsBuilder } from '@appello/common';
A Hook for manager query params with debounced time.
API:
import { useQueryParamsBuilder } from '@appello/common';
const Component = () => {
  const [getService, { data }] = useAnyRequest();
  const { updateQueryParams } = useQueryParamsBuilder({
    requestCb: params => getService(params),
    defaultParams: {
      categoryId,
      search: '',
    },
  });
  
  return (
    <></>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| requestCb | Any request function | () => Promise<any> | Required | 
| defaultParams | Any object. | AnyObject | Required | 
| options | Lodash options | DebounceOptions | Optional | 
Return:
| Property | Description | Type | 
|---|---|---|
| loading | Loading promise. | boolean | 
| queryParams | Object with query params. | AnyObject | 
| updateQueryParams | Update query params with. | (params: Partial<T>, withDebounce = false) => void | 
| reset | Reset query params object. | () => void | 
useSelectOptions
import { useSelectOptions } from '@appello/common';
Converts dictionaries for picker to structure:
[
  {
    value: 1,
    label: 'Select 1'
  }, {
    value: 2,
    label: 'Select 2'
  }
]API:
import { useSelectOptions } from '@appello/common';
const Component = () => {
  const convertedOptions = useSelectOptions([
    {
      val: 1,
      text: 'Select 1',
    },
    {
      val: 2,
      text: 'Select 2',
    },
  ], {
    label: 'text',
    value: 'val',
  })
  return (
    <span>{JSON.stringify(convertedOptions)}</span>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| collection | Collection of objects to be converted. | AnyObject[] | Required | 
| object | Key Matching Object. | {label: string, value: string} | Required | 
Return:
| Property | Description | Type | 
|---|---|---|
| newCollection | Converted collection. | {label: string, value: string}[] | 
useStateObject
import { useStateObject } from '@appello/common';
Hook with state for working with objects.
API:
import { useStateObject } from "@appello/common";
import { setState } from "jest-circus";
const Component = () => {
  const [state, setState] = useStateObject({
    name: "Test",
    lastName: "Jest",
    age: 21
  });
  return (
    <>
      <span>{JSON.stringify(state)}</span>
      <button
        type="button"
        onClick={() => {
          setState(prev => ({
            age: prev.age + 25
          }));
        }}
      >
        Update using callback
      </button>
      <button
        type="button"
        onClick={() => {
          setState({
            name: "New",
            lastName: "Test"
          });
        }}
      >
        Update using object
      </button>
    </>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| object | Default object. | AnyObject | Required | 
Return:
| Property | Description | Type | 
|---|---|---|
| state | Returns a stateful value, and a function to update it. | [AnyObject, Dispatch<SetStateAction<AnyObject>>] | 
useStep
import { useStep } from '@appello/common';
Custom hook that manages steps in a multistep process.
API:
import { useStep } from "@appello/common";
const Component = () => {
  const { step, increment, decrement } = useStep(0);
  return (
    <>
      <p>Current step: {step}</p>
      <p>
        <button type="button" onClick={increment}>
          Increment
        </button>
        <button type="button" onClick={decrement} style={{ margin: "0 8px" }}>
          Decrement
        </button>
      </p>
    </>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| initialValue | Initial value. | boolean | 0 | 
Return:
| Property | Description | Type | 
|---|---|---|
| step | Current step. | number | 
| increment | Increase the step by one. | () => void | 
| decrement | Reduce the pitch by one. | () => void | 
| reset | Reset steps to default value. | () => void | 
| setStep | Set step manually. | Dispatch<SetStateAction<number>> | 
useSwitchValue
import { useSwitchValue } from '@appello/common';
A hook that toggle states.
API:
import { useSwitchValue } from "@appello/common";
const Component = () => {
  const { value, on, off, toggle } = useSwitchValue(false);
  return (
    <>
      <p>{`${value}`}</p>
      <p>
        <button type="button" onClick={on}>
          On
        </button>
        <button type="button" onClick={off} style={{ margin: "0 8px" }}>
          Off
        </button>
        <button type="button" onClick={toggle}>
          Toggle
        </button>
      </p>
    </>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| defaultValue | Default value. | boolean | Required | 
Return:
| Property | Description | Type | 
|---|---|---|
| value | Switching value. | boolean | 
| on | Set value true. | () => void | 
| off | Set value false. | () => void | 
| toggle | Toggle value. | () => void | 
| set | Set value manually. | Dispatch<SetStateAction<boolean>> | 
useUnmountEffect
import { useUnmountEffect } from '@appello/common';
A hook that executes the function right before the component is unmounted.
API:
import { useUnmountEffect } from "@appello/common";
const Component = () => {
  useUnmountEffect(() => {
    console.log('Unmount');
  });
  return <p>Hello World!</p>;
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| callback | Callback that is called when the component is unmounted. | () => void | Required | 
useUpdateEffect
import { useUpdateEffect } from '@appello/common';
A hook alike useEffect but skips running the effect for the first time.
API:
import { useUpdateEffect } from "@appello/common";
const Component = () => {
  const [count, setCount] = useState(0);
  const [updateEffectCount, setUpdateEffectCount] = useState(0);
  useUpdateEffect(() => {
    setUpdateEffectCount((c) => c + 1);
  }, [count]);
  return (
    <div>
      <p>updateEffectCount: {updateEffectCount}</p>
      <button type="button" onClick={() => setCount((c) => c + 1)}>
        reRender
      </button>
    </div>
  );
};Params:
| Property | Description | Type | Default | 
|---|---|---|---|
| effect | Callback that is called when the component is updated. | () => void | Required | 
| deps | The dependencies list. | DependencyList | undefined | 
Types:
ElementType
import { ElementType } from '@appello/common';
The type returns any HTML element.
Usage:
import { ElementType } from '@appello/common';
const target: ElementType = document.getElementById('#user');Nullable
import { Nullable } from '@appello/common';
The type returns either null or generic.
Usage:
import { Nullable } from '@appello/common';
type User = {
  name: string;
  lastName: string; 
}
const user: Nullable<User> = null;Nillable
import { Nillable } from '@appello/common';
The type returns either null or undefined or generic.
Usage:
import { Nillable } from '@appello/common';
type User = {
  name: string;
  lastName: string; 
}
const user: Nillable<User> = null;
const user2: Nillable<User> = undefined;NotNullable
import { NotNullable } from '@appello/common';
Prohibits the type from being null, only generic.
Usage:
import { NotNullable } from '@appello/common';
type User = {
  name: string;
  lastName: string; 
}
const user: NotNullable<User> = {
  name: string,
  lastName: string,
};NotNillable
import { NotNillable } from '@appello/common';
Prohibits the type from being null or undefined, only generic.
Usage:
import { NotNillable } from '@appello/common';
type User = {
  name: string;
  lastName: string; 
}
const user: NotNillable<User> = {
  name: string,
  lastName: string,
};Unidentifiable
import { Unidentifiable } from '@appello/common';
The type returns either undefined or generic.
Usage:
import { Unidentifiable } from '@appello/common';
type User = {
  name: string;
  lastName: string; 
}
const user: Unidentifiable<User> = undefined;Negative
import { Negative } from '@appello/common';
Entity can match undefined, null, false or generic.
Usage:
import { Negative } from '@appello/common';
type User = {
  name: string;
  lastName: string; 
}
const user: Negative<User> = undefined;
const user1: Negative<User> = null;
const user2: Negative<User> = false;
const user3: Negative<User> = {
  name: string,
  lastName: string,
};ValueOf
import { ValueOf } from '@appello/common';
Get values from any type.
Usage:
import { ValueOf } from '@appello/common';
type User = {
  name: string;
  age: number; 
}
const userName: ValueOf<User> = 'User';
const userAge: ValueOf<User> = 22;AnyObject
import { AnyObject } from '@appello/common';
Common type for objects if we don't know an object type.
Usage:
import { AnyObject } from '@appello/common';
const user: AnyObject = {
  name: 'User',
  age: 123,
  lastName: 'User 2',
}AnyPromise
import { AnyPromise } from '@appello/common';
Can be used for any promises.
Usage:
import { AnyPromise } from '@appello/common';
const getUser: AnyPromise = Promise.resolve('User'); PartialRecord
import { PartialRecord } from '@appello/common';
It can be used to create a type that represents a partial object.
Usage:
import { PartialRecord } from '@appello/common';
type Keys = 'name' | 'age' | 'address';
type Value = string;
type PartialPerson = PartialRecord<Keys, Value>;
const person1: PartialPerson = {};
const person2: PartialPerson = { name: 'John' };
const person3: PartialPerson = { age: '30', address: '123 Main St' };PartialDeep
import { PartialDeep } from '@appello/common';
This type allows you to create partial versions of nested objects.
Usage:
import { PartialDeep } from '@appello/common';
interface User {
  name: string;
  address: {
    city: string;
    country: string;
  };
}
const user: PartialDeep<User> = {
  address: {
    city: 'New York'
  }
};ResponseErrors
import { ResponseErrors } from '@appello/common';
Type for object where the key is a string, and the value is also a string.
Usage:
import { ResponseErrors } from '@appello/common';
const error: ResponseErrors = {
  text: 'Something was wrong',
};UnknownFunction
import { UnknownFunction } from '@appello/common';
Type for an unknown function with unknown parameters.
Usage:
import { UnknownFunction } from '@appello/common';
const unknownFunction: UnknownFunction = props => {
  console.log(props);
};AnyFunction
import { AnyFunction } from '@appello/common';
Type for any function (best to avoid this).
Usage:
import { AnyFunction } from '@appello/common';
const anyFunction: AnyFunction = props => {
  console.log(props);
};PromiseType
import { PromiseType } from '@appello/common';
This type extracts the value type from the Promise.
Usage:
import { PromiseType } from '@appello/common';
type User = { 
  id: number; 
  name: string 
}
async function fetchUser(): Promise<> {
  return { id: 1, name: 'John' };
}
type UserType = PromiseType<ReturnType<typeof fetchUser>>;RequiredKeys / OptionalKeys
import { RequiredKeys, OptionalKeys } from '@appello/common';
This type separates mandatory and optional object keys.
Usage:
import { RequiredKeys, OptionalKeys } from '@appello/common';
type User = {
  id: number;
  name?: string;
  email?: string;
}
type UserRequiredKeys = RequiredKeys<User>; // "id"
type UserOptionalKeys = OptionalKeys<User>; // "name" | "email"Merge
import { Merge } from '@appello/common';
This type combines two types, where properties of first type override properties of a second type.
Usage:
import { Merge } from '@appello/common';
type User = {
  name: string;
  age: number;
}
type Admin = {
  isAdmin: boolean;
}
type UserAdmin = Merge<A, B>;
const user1: UserAdmin = {
  name: 'hello',
  age: 12,
  isAdmin: true,
};AsyncReturnType
import { AsyncReturnType } from '@appello/common';
This type retrieves the return type of asynchronous function.
Usage:
import { AsyncReturnType } from '@appello/common';
async function getUser() {
  return { id: 1, name: 'John' };
}
type UserType = AsyncReturnType<typeof getUser>;FirstArgument
import { FirstArgument } from '@appello/common';
This type retrieves the type of the function's first argument.
Usage:
import { FirstArgument } from '@appello/common';
function logUser(user: { id: number; name: string }) {
  console.log(user);
}
type UserArg = FirstArgument<typeof logUser>;11 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago