| @@ -53,6 +53,7 @@ import { fetchMineProfile } from "../../store/actions/profile/profileActions"; | |||
| import CreateOffer from "../Cards/CreateOfferCard/CreateOffer"; | |||
| import { Drawer as HeaderDrawer } from "./Drawer/Drawer"; | |||
| import useSearch from "../../hooks/useOffers/useSearch"; | |||
| import { routeMatches } from "../../util/helpers/routeHelpers"; | |||
| // import useQueryString from "../../hooks/useOffers/useQueryString"; | |||
| const Header = () => { | |||
| @@ -132,12 +133,12 @@ const Header = () => { | |||
| useEffect(() => { | |||
| let shouldShowHeader = true; | |||
| if ( | |||
| location.pathname === LOGIN_PAGE || | |||
| location.pathname === REGISTER_PAGE || | |||
| location.pathname === REGISTER_SUCCESSFUL_PAGE || | |||
| location.pathname === FORGOT_PASSWORD_PAGE || | |||
| location.pathname === FORGOT_PASSWORD_MAIL_SENT || | |||
| location.pathname === RESET_PASSWORD_PAGE | |||
| routeMatches(LOGIN_PAGE) || | |||
| routeMatches(REGISTER_PAGE) || | |||
| routeMatches(REGISTER_SUCCESSFUL_PAGE) || | |||
| routeMatches(FORGOT_PASSWORD_PAGE) || | |||
| routeMatches(FORGOT_PASSWORD_MAIL_SENT) || | |||
| routeMatches(RESET_PASSWORD_PAGE) | |||
| ) { | |||
| shouldShowHeader = false; | |||
| } | |||
| @@ -173,8 +174,8 @@ const Header = () => { | |||
| }; | |||
| const handleSearch = (value) => { | |||
| if ( | |||
| history.location.pathname !== HOME_PAGE && | |||
| history.location.pathname !== BASE_PAGE | |||
| !routeMatches(HOME_PAGE) && | |||
| !routeMatches(BASE_PAGE) | |||
| ) { | |||
| const newQueryString = new URLSearchParams({ search: value }); | |||
| history.push({ | |||
| @@ -12,6 +12,7 @@ import { fetchCategories } from "../../store/actions/categories/categoriesAction | |||
| import { fetchLocations } from "../../store/actions/locations/locationsActions"; | |||
| import { | |||
| selectOffers, | |||
| selectPinnedOffers, | |||
| selectTotalOffers, | |||
| } from "../../store/selectors/offersSelectors"; | |||
| import useFilters from "./useFilters"; | |||
| @@ -35,6 +36,7 @@ const useOffers = () => { | |||
| const dispatch = useDispatch(); | |||
| const filters = useFilters(); | |||
| const queryStringHook = useQueryString(); | |||
| const pinnedOffers = useSelector(selectPinnedOffers); | |||
| const offers = useSelector(selectOffers); | |||
| const totalOffers = useSelector(selectTotalOffers); | |||
| const history = useHistory(); | |||
| @@ -52,7 +54,7 @@ const useOffers = () => { | |||
| if (history.location.state?.logo) { | |||
| clear(); | |||
| } | |||
| }, [history.location]) | |||
| }, [history.location]); | |||
| // On every change of query string, new header string should be created | |||
| // Header string is shown on Home page above offers | |||
| @@ -96,15 +98,15 @@ const useOffers = () => { | |||
| }, [queryStringHook.isInitiallyLoaded]); | |||
| const allOffersToShow = useMemo(() => { | |||
| return offers; | |||
| }, [offers]); | |||
| return [...pinnedOffers, ...offers]; | |||
| }, [offers, pinnedOffers]); | |||
| useEffect(() => { | |||
| if (filtersCleared) { | |||
| setFiltersCleared(false); | |||
| apply(); | |||
| } | |||
| }, [filtersCleared]) | |||
| }, [filtersCleared]); | |||
| const apply = () => { | |||
| filters.apply(); | |||
| @@ -120,7 +122,7 @@ const useOffers = () => { | |||
| const clearFiltersAndApply = () => { | |||
| clear(); | |||
| setFiltersCleared(true); | |||
| } | |||
| }; | |||
| // Those hooks are below becouse function apply cannot be put on props before initialization | |||
| const sorting = useSorting(apply); | |||
| @@ -162,6 +162,7 @@ export default { | |||
| offers: { | |||
| getOneOffer: "offers", | |||
| getOffers: "offers", | |||
| getFeaturedOffers: "offers/featured", | |||
| addOffer: "offers", | |||
| categories: "categories", | |||
| locations: "locations", | |||
| @@ -5,6 +5,10 @@ export const attemptFetchOffers = (payload) => { | |||
| if (payload) return getRequest(apiEndpoints.offers.getOffers + payload); | |||
| return getRequest(apiEndpoints.offers.getOffers); | |||
| }; | |||
| export const attemptFetchFeaturedOffers = (payload) => { | |||
| if (payload) return getRequest(apiEndpoints.offers.getFeaturedOffers + payload); | |||
| return getRequest(apiEndpoints.offers.getOffers); | |||
| } | |||
| export const attemptFetchOneOffer = (payload) => { | |||
| const url = `${apiEndpoints.offers.getOneOffer}/${payload}`; | |||
| return getRequest(url); | |||
| @@ -12,6 +12,17 @@ export const OFFERS_SUCCESS = createSuccessType(OFFERS_SCOPE); | |||
| export const OFFERS_ERROR = createErrorType(OFFERS_SCOPE); | |||
| export const OFFERS_CLEAR = createClearType(OFFERS_SCOPE); | |||
| export const OFFERS_FEATURED_SCOPE = "OFFERS_FEATURED_SCOPE"; | |||
| export const OFFERS_FEATURED_FETCH = createFetchType(OFFERS_FEATURED_SCOPE); | |||
| export const OFFERS_FEATURED_SUCCESS = createSuccessType(OFFERS_FEATURED_SCOPE); | |||
| export const OFFERS_FEATURED_ERROR = createErrorType(OFFERS_FEATURED_SCOPE); | |||
| export const OFFERS_FEATURED_CLEAR = createClearType(OFFERS_FEATURED_SCOPE); | |||
| export const OFFERS_ALL_SCOPE = "OFFERS_ALL_SCOPE"; | |||
| export const OFFERS_ALL_FETCH = createFetchType(OFFERS_ALL_SCOPE); | |||
| export const OFFERS_ALL_SUCCESS = createSuccessType(OFFERS_ALL_SCOPE); | |||
| export const OFFERS_ALL_ERROR = createErrorType(OFFERS_ALL_SCOPE); | |||
| export const OFFERS_MORE_SCOPE = "OFFERS_MORE_SCOPE"; | |||
| export const OFFERS_FETCH_MORE = createFetchType(OFFERS_MORE_SCOPE); | |||
| export const OFFERS_FETCH_MORE_SUCCESS = createSuccessType(OFFERS_MORE_SCOPE) | |||
| @@ -35,12 +46,17 @@ export const ONE_OFFER_ERROR = createErrorType(ONE_OFFER_SCOPE); | |||
| export const OFFERS_PINNED_SET = createSetType("OFFERS_PINNED_SET"); | |||
| export const OFFERS_PINNED_ADD = createSetType("OFFERS_PINNED_ADD"); | |||
| export const OFFERS_SET = createSetType("OFFERS_SET"); | |||
| export const OFFERS_FEATURED_SET = createSetType("OFFERS_FEATURED_SET"); | |||
| export const OFFER_SET = createSetType("OFFER_SET"); | |||
| export const OFFERS_ADD = createSetType("OFFERS_ADD"); | |||
| export const OFFERS_NO_MORE = createSetType("OFFERS_NO_MORE"); | |||
| export const OFFERS_SET_TOTAL = createSetType("OFFERS_SET_TOTAL"); | |||
| export const OFFERS_PROFILE_SET = createSetType("OFFERS_PROFILE_SET"); | |||
| export const OFFERS_MINE_SET = createSetType("OFFERS_MY_ADD"); | |||
| export const OFFER_INDEX_SET = createSetType("OFFER_INDEX_SET"); | |||
| export const OFFER_INDEX_CLEAR = createClearType("OFFER_INDEX_CLEAR"); | |||
| export const OFFER_PAGE_SET = createSetType("OFFER_PAGE_SET"); | |||
| export const OFFER_FEATURED_PAGE_SET = createSetType("OFFER_FEATURED_PAGE_SET"); | |||
| export const OFFER_ADD_SCOPE = "OFFER_ADD_SCOPE"; | |||
| export const OFFER_ADD = createFetchType(OFFER_ADD_SCOPE); | |||
| @@ -2,6 +2,11 @@ import { | |||
| OFFERS_ADD, | |||
| OFFERS_CLEAR, | |||
| OFFERS_ERROR, | |||
| OFFERS_FEATURED_CLEAR, | |||
| OFFERS_FEATURED_ERROR, | |||
| OFFERS_FEATURED_FETCH, | |||
| OFFERS_FEATURED_SET, | |||
| OFFERS_FEATURED_SUCCESS, | |||
| OFFERS_FETCH, | |||
| OFFERS_FETCH_MORE, | |||
| OFFERS_FETCH_MORE_ERROR, | |||
| @@ -26,6 +31,8 @@ import { | |||
| OFFER_EDIT, | |||
| OFFER_EDIT_ERROR, | |||
| OFFER_EDIT_SUCCESS, | |||
| OFFER_FEATURED_PAGE_SET, | |||
| OFFER_PAGE_SET, | |||
| OFFER_REMOVE, | |||
| OFFER_REMOVE_ERROR, | |||
| OFFER_REMOVE_SUCCESS, | |||
| @@ -52,6 +59,37 @@ export const clearOffers = () => ({ | |||
| type: OFFERS_CLEAR, | |||
| }); | |||
| // Fetch offers | |||
| export const fetchAllOffers = (payload) => ({ | |||
| type: OFFERS_FETCH, | |||
| payload, | |||
| }); | |||
| export const fetchAllOffersSuccess = (payload) => ({ | |||
| type: OFFERS_SUCCESS, | |||
| payload, | |||
| }); | |||
| export const fetchAllOffersError = (payload) => ({ | |||
| type: OFFERS_ERROR, | |||
| payload, | |||
| }); | |||
| // Fetch featured offers | |||
| export const fetchFeaturedOffers = (payload) => ({ | |||
| type: OFFERS_FEATURED_FETCH, | |||
| payload, | |||
| }); | |||
| export const fetchFeaturedOffersSuccess = (payload) => ({ | |||
| type: OFFERS_FEATURED_SUCCESS, | |||
| payload, | |||
| }); | |||
| export const fetchFeaturedOffersError = (payload) => ({ | |||
| type: OFFERS_FEATURED_ERROR, | |||
| payload, | |||
| }); | |||
| export const clearFeaturedOffers = () => ({ | |||
| type: OFFERS_FEATURED_CLEAR, | |||
| }); | |||
| // Fetch more offers | |||
| export const fetchMoreOffers = (payload) => ({ | |||
| type: OFFERS_FETCH_MORE, | |||
| @@ -176,3 +214,15 @@ export const setOffer = (payload) => ({ | |||
| type: OFFER_SET, | |||
| payload, | |||
| }); | |||
| export const setFeaturedOffers = (payload) => ({ | |||
| type: OFFERS_FEATURED_SET, | |||
| payload | |||
| }) | |||
| export const setOfferPage = (payload) => ({ | |||
| type: OFFER_PAGE_SET, | |||
| payload | |||
| }) | |||
| export const setFeaturedOfferPage = (payload) => ({ | |||
| type: OFFER_FEATURED_PAGE_SET, | |||
| payload | |||
| }) | |||
| @@ -14,6 +14,12 @@ import { | |||
| ONE_OFFER_SUCCESS, | |||
| OFFERS_SET_TOTAL, | |||
| OFFERS_PROFILE_SET, | |||
| OFFERS_FEATURED_CLEAR, | |||
| OFFERS_FEATURED_SET, | |||
| OFFER_INDEX_SET, | |||
| OFFER_INDEX_CLEAR, | |||
| OFFER_PAGE_SET, | |||
| OFFER_FEATURED_PAGE_SET, | |||
| } from "../../actions/offers/offersActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| @@ -25,8 +31,11 @@ const initialState = { | |||
| total: 0, | |||
| error: "", | |||
| newOffer: "", | |||
| selectedOffer:"", | |||
| selectedOffer: "", | |||
| noMoreOffers: false, | |||
| offerIndex: null, | |||
| offerPage: null, | |||
| featuredOfferPage: null, | |||
| }; | |||
| export default createReducer( | |||
| @@ -46,6 +55,12 @@ export default createReducer( | |||
| [OFFERS_SET_TOTAL]: setTotalOffers, | |||
| [OFFERS_MINE_SET]: setMineOffers, | |||
| [OFFERS_PROFILE_SET]: setProfileOffers, | |||
| [OFFERS_FEATURED_CLEAR]: clearFeaturedOffers, | |||
| [OFFERS_FEATURED_SET]: setFeaturedOffers, | |||
| [OFFER_INDEX_SET]: setOfferIndex, | |||
| [OFFER_INDEX_CLEAR]: clearOfferIndex, | |||
| [OFFER_PAGE_SET]: setOfferPage, | |||
| [OFFER_FEATURED_PAGE_SET]: setFeaturedOfferPage, | |||
| }, | |||
| initialState | |||
| ); | |||
| @@ -54,8 +69,8 @@ function fetchOffersError(state, action) { | |||
| return { ...state, error: action.payload }; | |||
| } | |||
| function clearOffers() { | |||
| return initialState; | |||
| function clearOffers(state) { | |||
| return { ...state, offers: [] }; | |||
| } | |||
| function setOffers(state, action) { | |||
| return { | |||
| @@ -72,67 +87,104 @@ function setOffer(state, action) { | |||
| function addOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| offers: [...state.offers, ...action.payload] | |||
| } | |||
| offers: [...state.offers, ...action.payload], | |||
| }; | |||
| } | |||
| function setPinnedOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| pinnedOffers: [...action.payload] | |||
| } | |||
| pinnedOffers: [...action.payload], | |||
| }; | |||
| } | |||
| function addOffer(state, action) { | |||
| return { | |||
| ...state, | |||
| offer: action.payload | |||
| } | |||
| offer: action.payload, | |||
| }; | |||
| } | |||
| function fetchOneOffer(state,action) { | |||
| function fetchOneOffer(state, action) { | |||
| return { | |||
| ...state, | |||
| selectedOffer: action.payload | |||
| } | |||
| selectedOffer: action.payload, | |||
| }; | |||
| } | |||
| function fetchOneOfferSuccess( state, action) { | |||
| function fetchOneOfferSuccess(state, action) { | |||
| return { | |||
| ...state, | |||
| selectedOffer: action.payload | |||
| } | |||
| selectedOffer: action.payload, | |||
| }; | |||
| } | |||
| function fetchOneOfferError (state, action) { | |||
| function fetchOneOfferError(state, action) { | |||
| return { | |||
| ...state, | |||
| error: action.payload | |||
| } | |||
| error: action.payload, | |||
| }; | |||
| } | |||
| function addPinnedOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| pinnedOffers: [...state.pinnedOffers, ...action.payload] | |||
| } | |||
| pinnedOffers: [...state.pinnedOffers, ...action.payload], | |||
| }; | |||
| } | |||
| function setNoMoreOffersStatus(state, action) { | |||
| return { | |||
| ...state, | |||
| noMoreOffers: action.payload | |||
| } | |||
| noMoreOffers: action.payload, | |||
| }; | |||
| } | |||
| function setTotalOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| total: action.payload | |||
| } | |||
| total: action.payload, | |||
| }; | |||
| } | |||
| function setMineOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| mineOffers: action.payload | |||
| } | |||
| mineOffers: action.payload, | |||
| }; | |||
| } | |||
| function setProfileOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| profileOffers: action.payload | |||
| profileOffers: action.payload, | |||
| }; | |||
| } | |||
| function setFeaturedOffers(state, action) { | |||
| return { | |||
| ...state, | |||
| pinnedOffers: action.payload | |||
| } | |||
| } | |||
| function clearFeaturedOffers(state) { | |||
| return { | |||
| ...state, | |||
| pinnedOffers: [] | |||
| } | |||
| } | |||
| function setOfferIndex(state, {payload}) { | |||
| return { | |||
| ...state, | |||
| offerIndex: payload | |||
| } | |||
| } | |||
| function clearOfferIndex(state) { | |||
| return { | |||
| ...state, | |||
| offerIndex: null, | |||
| } | |||
| } | |||
| function setOfferPage(state, {payload}) { | |||
| return { | |||
| ...state, | |||
| offerPage: payload | |||
| } | |||
| } | |||
| function setFeaturedOfferPage(state, {payload}) { | |||
| return { | |||
| ...state, | |||
| featuredOfferPage: payload | |||
| } | |||
| } | |||
| @@ -1,11 +1,14 @@ | |||
| import { | |||
| attemptAddOffer, | |||
| attemptEditOffer, | |||
| attemptFetchFeaturedOffers, | |||
| attemptFetchOffers, | |||
| attemptFetchOneOffer, | |||
| attemptRemoveOffer, | |||
| } from "../../request/offersRequest"; | |||
| import { | |||
| // OFFERS_ALL_FETCH, | |||
| OFFERS_FEATURED_FETCH, | |||
| OFFERS_FETCH, | |||
| OFFERS_PROFILE_FETCH, | |||
| OFFER_ADD, | |||
| @@ -32,6 +35,10 @@ import { | |||
| fetchProfileOffersError, | |||
| removeOfferError, | |||
| editOfferError, | |||
| clearFeaturedOffers, | |||
| // fetchAllOffersSuccess, | |||
| // fetchAllOffersError, | |||
| // setFeaturedOfferPage, | |||
| } from "../actions/offers/offersActions"; | |||
| import { all, takeLatest, call, put, select } from "@redux-saga/core/effects"; | |||
| import { | |||
| @@ -55,12 +62,59 @@ import { | |||
| } from "../actions/offers/offersActions"; | |||
| import { selectUserId } from "../selectors/loginSelectors"; | |||
| import { | |||
| // selectFeaturedOfferPage, | |||
| // selectOfferPage, | |||
| selectOffers, | |||
| selectPinnedOffers, | |||
| selectTotalOffers, | |||
| } from "../selectors/offersSelectors"; | |||
| import history from "../utils/history"; | |||
| import { NOT_FOUND_PAGE } from "../../constants/pages"; | |||
| // import { KEY_PAGE } from "../../constants/queryStringConstants"; | |||
| // function* fetchAllOffers(payload) { | |||
| // try { | |||
| // yield put(clearOffers()); | |||
| // yield put(clearFeaturedOffers()); | |||
| // const queryString = new URLSearchParams( | |||
| // convertQueryStringForBackend(payload.payload.queryString) | |||
| // ); | |||
| // const page = queryString.get(KEY_PAGE); | |||
| // const offerPage = yield select(selectOfferPage) | |||
| // const featuredOfferPage = yield select(selectFeaturedOfferPage); | |||
| // const featuredOffers = yield call( | |||
| // attemptFetchFeaturedOffers, | |||
| // "?" + removePageAndSizeHelper(queryString.toString()) | |||
| // ); | |||
| // const newQueryString = new URLSearchParams( | |||
| // convertQueryStringForBackend(payload.payload.queryString) | |||
| // ); | |||
| // const regularOffers = yield call( | |||
| // attemptFetchOffers, | |||
| // "?" + queryString.toString() | |||
| // ); | |||
| // const offerIndex = featuredOffers.data.length - page * 10; | |||
| // if (page > Math.floor(featuredOffers.data.length - (page - 1) * 10)) { | |||
| // if (featuredOffers.data.length !== 0) { | |||
| // yield put( | |||
| // setPinnedOffers( | |||
| // featuredOffers.data.slice((page - 1) * 10), | |||
| // featuredOffers.data.length | |||
| // ) | |||
| // ); | |||
| // yield put(setOffers(regularOffers.data.items.slice())); | |||
| // } | |||
| // } | |||
| // yield put( | |||
| // setTotalOffers(regularOffers.data.total + featuredOffers.data.length) | |||
| // ); | |||
| // yield put(setPinnedOffers(featuredOffers.data.items.pinnedOffers)); | |||
| // yield put(fetchAllOffersSuccess()); | |||
| // } catch (e) { | |||
| // yield put(fetchAllOffersError()); | |||
| // yield call(console.log, e); | |||
| // } | |||
| // } | |||
| function* fetchOffers(payload) { | |||
| try { | |||
| @@ -73,8 +127,26 @@ function* fetchOffers(payload) { | |||
| "?" + newQueryString.toString() | |||
| ); | |||
| yield put(setTotalOffers(data.data.total)); | |||
| yield put(setOffers(data.data.items.regularOffers)); | |||
| yield put(setPinnedOffers(data.data.items.pinnedOffers)); | |||
| yield put(setOffers(data.data.offers.filter(offer => !offer.pinned))); | |||
| yield put(setPinnedOffers(data.data.offers.filter(offer => offer.pinned))); | |||
| yield put(fetchOffersSuccess()); | |||
| } catch (e) { | |||
| yield put(fetchOffersError()); | |||
| yield call(console.log, e); | |||
| } | |||
| } | |||
| function* fetchFeaturedOffers(payload) { | |||
| try { | |||
| yield put(clearFeaturedOffers()); | |||
| const newQueryString = new URLSearchParams( | |||
| convertQueryStringForBackend(payload.payload.queryString) | |||
| ); | |||
| const data = yield call( | |||
| attemptFetchFeaturedOffers, | |||
| "?" + newQueryString.toString() | |||
| ); | |||
| yield put(setPinnedOffers(data.data)); | |||
| yield put(fetchOffersSuccess()); | |||
| } catch (e) { | |||
| yield put(fetchOffersError()); | |||
| @@ -210,5 +282,7 @@ export default function* offersSaga() { | |||
| takeLatest(OFFERS_PROFILE_FETCH, fetchProfileOffers), | |||
| takeLatest(OFFER_REMOVE, removeOffer), | |||
| takeLatest(OFFER_EDIT, editOffer), | |||
| takeLatest(OFFERS_FEATURED_FETCH, fetchFeaturedOffers), | |||
| // takeLatest(OFFERS_ALL_FETCH, fetchAllOffers), | |||
| ]); | |||
| } | |||
| @@ -36,3 +36,11 @@ export const selectProfileOffers = createSelector( | |||
| offersSelector, | |||
| (state) => state.profileOffers | |||
| ) | |||
| export const selectFeaturedOfferPage = createSelector( | |||
| offersSelector, | |||
| (state) => state.featuredOfferPage | |||
| ) | |||
| export const selectOfferPage = createSelector( | |||
| offersSelector, | |||
| (state) => state.offerPage | |||
| ) | |||
| @@ -247,3 +247,24 @@ export const makeQueryStringHelper = (filters, paging, search, sorting) => { | |||
| newQueryString.append(KEY_SEARCH, search.searchString ?? ""); | |||
| return newQueryString; | |||
| }; | |||
| export const changePageQueryStringHelper = (queryString, newPage) => { | |||
| const newQueryString = new URLSearchParams(queryString); | |||
| if (newQueryString.has(KEY_PAGE)) { | |||
| newQueryString.delete(KEY_PAGE); | |||
| } | |||
| newQueryString.set(KEY_PAGE, newPage); | |||
| return newQueryString.toString(); | |||
| }; | |||
| export const changePageQueryObjectHelper = (queryObject, newPage) => { | |||
| if (queryObject.has(KEY_PAGE)) { | |||
| queryObject.delete(KEY_PAGE); | |||
| } | |||
| queryObject.set(KEY_PAGE, newPage); | |||
| return queryObject; | |||
| }; | |||
| export const removePageAndSizeHelper = (queryString) => { | |||
| const newQueryString = new URLSearchParams(queryString); | |||
| if (newQueryString.has(KEY_PAGE)) newQueryString.delete(KEY_PAGE); | |||
| if (newQueryString.has(KEY_SIZE)) newQueryString.delete(KEY_SIZE); | |||
| return newQueryString.toString(); | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| import history from "../../store/utils/history"; | |||
| export const routeMatches = (route) => { | |||
| if ( | |||
| history.location.pathname === route || | |||
| history.location.pathname + "/" === route || | |||
| history.location.pathname.slice(0, -1) === route | |||
| ) | |||
| return true; | |||
| return false; | |||
| }; | |||