diff --git a/.env.example b/.env.example
index 4bbccf73..cc50bfdb 100644
--- a/.env.example
+++ b/.env.example
@@ -2,6 +2,7 @@ NODE_ENV=
API_HOST=
REACT_NATIVE_APP_VERSION_NR=
REACT_NATIVE_APP_AUTH0_DOMAIN=
+REACT_NATIVE_APP_AUTH0_AUDIENCE=
REACT_NATIVE_APP_AUTH0_SCHEME=
REACT_NATIVE_APP_AUTH0_CLIENT_ID=
REACT_NATIVE_APP_ENCRYPTION_KEY=
diff --git a/package.json b/package.json
index ff5bb22a..daf80978 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"@tanstack/query-sync-storage-persister": "^4.14.5",
"@tanstack/react-query": "^4.14.1",
"@tanstack/react-query-persist-client": "^4.14.5",
- "axios": "^1.1.3",
+ "axios": "1.2.0-alpha.1",
"color": "^4.2.3",
"date-fns": "^2.29.3",
"i18next": "^22.0.4",
diff --git a/src/App.tsx b/src/App.tsx
index c9471759..2303214c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,7 +7,7 @@ import { ThemeProvider } from 'styled-components/native';
import { AuthenticationProvider } from './_context';
import { StorageKey } from './_models';
-// import { QueryClientProvider } from './_providers';
+import { QueryClientProvider } from './_providers';
import RootStackNavigator from './_routing';
import { theme } from './_styles/theme';
import { storage } from './storage';
@@ -24,15 +24,14 @@ const App = () => {
return (
- {/* @TODO: Uncomment when react-query is used in the source code */}
- {/* */}
-
-
-
-
-
-
- {/* */}
+
+
+
+
+
+
+
+
);
diff --git a/src/_hooks/usePubliqApi.ts b/src/_hooks/usePubliqApi.ts
new file mode 100644
index 00000000..46d6bbc0
--- /dev/null
+++ b/src/_hooks/usePubliqApi.ts
@@ -0,0 +1,38 @@
+import { useCallback } from 'react';
+import { Config } from 'react-native-config';
+import { QueryObserverOptions, useQuery } from '@tanstack/react-query';
+
+import { useAuthentication } from '../_context';
+import { HttpClient, TApiError } from '../_http';
+import { Headers, Params } from '../_http/HttpClient';
+
+type TGetOptions = {
+ enabled?: boolean;
+ headers?: Headers;
+ onError?: QueryObserverOptions['onError'];
+ onSuccess?: QueryObserverOptions['onSuccess'];
+ params?: Params;
+};
+
+export function usePubliqApi() {
+ const { accessToken } = useAuthentication();
+
+ const defaultHeaders: Headers = {
+ Authorization: `Bearer ${accessToken}`,
+ };
+
+ const get = useCallback(
+ (queryKey: unknown[], path: string, { headers = {}, params = {}, ...options }: TGetOptions = {}) => {
+ return useQuery({
+ enabled: !!accessToken && (options.enabled === undefined || options.enabled),
+ onError: options.onError,
+ onSuccess: options.onSuccess,
+ queryFn: async () => HttpClient.get(`${Config.API_HOST}${path}`, params, { ...defaultHeaders, ...headers }),
+ queryKey,
+ });
+ },
+ [Config.API_HOST, accessToken],
+ );
+
+ return { get };
+}
diff --git a/src/_http/HttpClient.ts b/src/_http/HttpClient.ts
index af89b0ad..a0658033 100644
--- a/src/_http/HttpClient.ts
+++ b/src/_http/HttpClient.ts
@@ -4,8 +4,8 @@ import axios, { AxiosError, AxiosResponse, ResponseType } from 'axios';
import { TApiError, TValidationError } from './HttpError';
import { HttpStatus } from './HttpStatus';
-type Params = Record;
-type Headers = Record;
+export type Params = Record;
+export type Headers = Record;
class HttpClient {
static getUrl(route: string): string {
diff --git a/src/_routing/_components/RootStackNavigator.tsx b/src/_routing/_components/RootStackNavigator.tsx
index efcef872..ed932d0c 100644
--- a/src/_routing/_components/RootStackNavigator.tsx
+++ b/src/_routing/_components/RootStackNavigator.tsx
@@ -4,12 +4,12 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useAuthentication } from '../../_context';
import { StorageKey } from '../../_models';
-import Home from '../../login/Login';
+import Login from '../../login/Login';
import Onboarding from '../../onboarding/Onboarding';
import { storage } from '../../storage';
import { MainNavigator } from './MainNavigator';
-export type TRootRoutes = 'MainNavigator' | 'Onboarding' | 'Home';
+export type TRootRoutes = 'MainNavigator' | 'Onboarding' | 'Login';
export type TRootParams = Record;
const RootStack = createNativeStackNavigator();
@@ -35,7 +35,7 @@ export const RootStackNavigator = () => {
name="Onboarding"
/>
)}
- {!isAuthenticated && }
+ {!isAuthenticated && }
{isAuthenticated && }
);
diff --git a/src/env.d.ts b/src/env.d.ts
index 37457fcc..79e03d29 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -7,6 +7,7 @@ declare module 'react-native-config' {
| 'NODE_ENV'
| 'REACT_NATIVE_APP_AUTH0_CLIENT_ID'
| 'REACT_NATIVE_APP_AUTH0_DOMAIN'
+ | 'REACT_NATIVE_APP_AUTH0_AUDIENCE'
| 'REACT_NATIVE_APP_ENCRYPTION_KEY'
| 'REACT_NATIVE_APP_VERSION_NR';
diff --git a/src/login/Login.tsx b/src/login/Login.tsx
index 4b3520de..d170cd9c 100644
--- a/src/login/Login.tsx
+++ b/src/login/Login.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
+import { Config } from 'react-native-config';
import { BrandLogo, DiagonalSplitView } from '../_components';
import { useAuthentication } from '../_context';
@@ -11,7 +12,7 @@ const Login = () => {
const handleLogin = async () => {
try {
- await authorize({ scope: 'openid profile email offline_access' });
+ await authorize({ audience: Config.REACT_NATIVE_APP_AUTH0_AUDIENCE, scope: 'openid profile email offline_access' });
} catch (e) {
// @TODO: general error handling?
console.error(e);
diff --git a/src/onboarding/Onboarding.tsx b/src/onboarding/Onboarding.tsx
index 11e4d482..f90a84dd 100644
--- a/src/onboarding/Onboarding.tsx
+++ b/src/onboarding/Onboarding.tsx
@@ -12,7 +12,7 @@ const Onboarding = () => {
const onPress = () => {
storage.set(StorageKey.IsPolicyApproved, true);
- navigation.navigate('Home');
+ navigation.navigate('Login');
};
return (
diff --git a/src/profile/Profile.tsx b/src/profile/Profile.tsx
index aa8d36ec..0ce5282a 100644
--- a/src/profile/Profile.tsx
+++ b/src/profile/Profile.tsx
@@ -1,22 +1,25 @@
import React from 'react';
-import { SafeAreaView, ScrollView, Text } from 'react-native';
+import { Text } from 'react-native';
-import { Button } from '../_components';
+import { Button, SafeAreaView } from '../_components';
import { useToggle } from '../_hooks';
+import { useGetMe } from './_queries/useGetMe';
import LogoutModal from './LogOutModal';
const Profile = () => {
const [logOutModalVisible, toggleLogOutModalVisible] = useToggle(false);
+ const { data, isLoading } = useGetMe();
+ console.log({ data, isLoading }); // @TODO: remove this console.log
return (
-
-
-
+ <>
+
{/* @TODO: This is placeholder content */}
This is the profile page
-
-
+
+
+ >
);
};
diff --git a/src/profile/_models/index.ts b/src/profile/_models/index.ts
new file mode 100644
index 00000000..ff8cba3a
--- /dev/null
+++ b/src/profile/_models/index.ts
@@ -0,0 +1 @@
+export * from './passholder';
diff --git a/src/profile/_models/passholder.ts b/src/profile/_models/passholder.ts
new file mode 100644
index 00000000..61bf1141
--- /dev/null
+++ b/src/profile/_models/passholder.ts
@@ -0,0 +1,120 @@
+export type TCity = {
+ /** Name of the city */
+ city: string;
+ /** Postalcode of the city */
+ postalCode: string;
+};
+
+export type TAddress = TCity & {
+ /** Postal box number. */
+ box: string;
+ /** ISO 3166-1 alpha-2 country code. */
+ country: string;
+ /** House number. */
+ number: string;
+ /** Street name of the address. */
+ street: string;
+};
+
+export type TCardSystemMembership = {
+ /** A region, usually one or multiple municipalities in Belgium, that uses UiTPAS and provides discounts and/or rewards. For example "Paspartoe" (Brussels), UiTPAS Leuven, UiTPAS Hasselt, UiTPAS Gent, and so on. */
+ cardSystem: {
+ /** Indicates whether this cardsystem allows online cardless registrations. */
+ allowsCardlessRegistration?: boolean;
+ /** Branding information of the card system */
+ branding?: {
+ /** URL to the logo of the card system */
+ logo: string;
+ /** Color code of the primary branding color. */
+ primaryColor: string;
+ /** Color code of the secondary branding color. */
+ secondaryColor: string;
+ };
+ /** List of cities that are part of this card system */
+ cities: TCity[];
+ /** ID of the card system */
+ id: number;
+ /** Links of the card system */
+ links: {
+ /** URL of the website of the card system */
+ website: string;
+ };
+ /** Name of the card system. */
+ name: string;
+ /** Indicates whether this is a permanent card system */
+ permanent?: boolean;
+ };
+ /** If the passholder has right to a social tariff, this object contains details like the end date. */
+ socialTariff?: {
+ /** Exact moment that the passholder's right to a social tariff expires. */
+ endDate: string;
+ /** If true, the passholder's right to a social tariff has completely expired (the end date has passed and the passholder is no longer in a grace period). */
+ expired: boolean;
+ /** When the end date of the right to a social tariff has passed, the passholder may still be in a grace period that they can buy tickets at a social tariff until their right to a social tariff has been renewed. */
+ inGracePeriod: boolean;
+ };
+ status?: 'ACTIVE' | 'BLOCKED';
+ /** The UiTPAS number of the card that is linked to this card system membership. It is possible to have a CardSystemMembership without a card. However, a passholder always has at least one CardSystemMembership with a card. */
+ uitpasNumber: string;
+};
+
+export type TPassHolder = {
+ /** Address that the passholder lives at. Always present in responses. Passholders living outside of Belgium (usually near the border) will only have a postalCode and city in their address. */
+ address: TCity | TAddress;
+ /** This field is always available in responses. */
+ cardSystemMemberships: TCardSystemMembership[];
+ /**
+ * Name of the municipality that the passholder lives in. Deprecated in favor of address.city.
+ * @deprecated
+ */
+ city?: string;
+ /** This field is always available in responses. */
+ creationDate: string;
+ /** Date that the passholder was born. */
+ dateOfBirth?: string;
+ /** Contact email address of the passholder. Not present for every passholder. Multiple passholders can have the same email address. */
+ email?: string;
+ /** First name of the passholder. */
+ firstName: string;
+ /** Gender of the passholder. */
+ gender?: 'MALE' | 'FEMALE' | 'X';
+ /** This field is always available in responses. */
+ id: string;
+ /** Unique national (Belgian) INSZ number of an individual passholder to look up. */
+ inszNumber?: string;
+ /** Last name of the passholder. */
+ name: string;
+ /** Human-readable name of the passholder's nationality. */
+ nationality?: string;
+ /** Permissions that the passholder has given to be contacted. */
+ optInPreferences?: {
+ /** Rewards, actions and events selected specifically for the passholder based on their UiTPAS history. */
+ infoMails: boolean;
+ /** Notification when you reach an important UiTPAS milestone, for example a specific amount of points or an exclusive reward becomes available to you. */
+ milestoneMails: boolean;
+ /** Sporadic post mail with information about UiTPAS. Will be sent to the passholder's postal address. */
+ post: boolean;
+ /** Important information about the functionality of UiTPAS. */
+ serviceMails: boolean;
+ /** Free (sporadic) SMS messages with rewards, actions and events selected specifically for the passholder based on their UiTPAS history. */
+ sms: boolean;
+ };
+ /** Phone number that the passholder has registered, for example for SMS alerts. */
+ phoneNumber?: string;
+ /** Amount of points the passholder has currently saved (and not used). This field is always available in responses. */
+ points: number;
+ /**
+ * Postal code of the municipality that the passholder lives in. Deprecated in favor of address.postalCode
+ * @deprecated
+ */
+ postalCode?: string;
+ /** An organisation that partners with UiTPAS to provide discounts and/or rewards, and/or allows points to be collected at their events. */
+ registrationOrganizer: {
+ /** Unique ID of an UiTPAS organizer. (Same as its ID in UiTdatabank) */
+ id: string;
+ /** Human-readable name of an UiTPAS organizer. */
+ name: string;
+ };
+ /** Whether or not the passholder has a an UiTID registered. This field is always available in responses. */
+ uitidStatus: 'REGISTERED' | 'UNREGISTERED';
+};
diff --git a/src/profile/_queries/useGetMe.ts b/src/profile/_queries/useGetMe.ts
new file mode 100644
index 00000000..c6848028
--- /dev/null
+++ b/src/profile/_queries/useGetMe.ts
@@ -0,0 +1,7 @@
+import { usePubliqApi } from '../../_hooks/usePubliqApi';
+import { TPassHolder } from '../_models';
+
+export function useGetMe() {
+ const api = usePubliqApi();
+ return api.get(['me'], '/passholders/me');
+}
diff --git a/yarn.lock b/yarn.lock
index 322203d3..23f29fcd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2178,10 +2178,10 @@ atob@^2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
-axios@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35"
- integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==
+axios@1.2.0-alpha.1:
+ version "1.2.0-alpha.1"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0-alpha.1.tgz#67ade13f4d84c26ca555e552f15e3f6fba849e0d"
+ integrity sha512-qt/7xkSQNBRKP26mt28cmSI1Y3jVtrQzu7oLjIyUHEdjpVeg100luMJrRpBlKlCmMd233Peu00mOkNC1OwXOrw==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"