feat: add media provider and get madia from use-media-controller hook
This commit is contained in:
parent
39bb647b5c
commit
9f43dd02d7
@ -67,6 +67,15 @@ export type TextResponse = Root<{
|
|||||||
_znachenie: string | null;
|
_znachenie: string | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export type MediaResponse = Root<{
|
||||||
|
_name: string;
|
||||||
|
_foto: {
|
||||||
|
id: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
_klyuchNeIzmenya: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type Team = Root<{
|
export type Team = Root<{
|
||||||
_foto: Image[];
|
_foto: Image[];
|
||||||
_zvanie: string;
|
_zvanie: string;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
|
import { MediaItem } from '@/shared/types/media.type';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Certificate,
|
Certificate,
|
||||||
Charity,
|
Charity,
|
||||||
@ -7,6 +9,7 @@ import {
|
|||||||
History,
|
History,
|
||||||
Image,
|
Image,
|
||||||
Job,
|
Job,
|
||||||
|
MediaResponse,
|
||||||
Partner,
|
Partner,
|
||||||
Review,
|
Review,
|
||||||
Select,
|
Select,
|
||||||
@ -88,6 +91,17 @@ export const presentTexts = (texts: TextResponse) =>
|
|||||||
value: item._znachenie,
|
value: item._znachenie,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const presentMedia = (media: MediaResponse) => {
|
||||||
|
return media.records.map((record) => ({
|
||||||
|
key: record._klyuchNeIzmenya,
|
||||||
|
name: record._name,
|
||||||
|
photo: {
|
||||||
|
id: record._foto.id,
|
||||||
|
url: record._foto.url,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
export const presentReviews = (reviews: Review) =>
|
export const presentReviews = (reviews: Review) =>
|
||||||
reviews.records.map((review) => ({
|
reviews.records.map((review) => ({
|
||||||
id: review.id,
|
id: review.id,
|
||||||
|
|||||||
@ -122,6 +122,19 @@ export const textsRequest = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const mediaRequest = {
|
||||||
|
_mediaKontent: {
|
||||||
|
records: {
|
||||||
|
_name: true,
|
||||||
|
_foto: {
|
||||||
|
id: true,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
_klyuchNeIzmenya: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const teamRequest = {
|
export const teamRequest = {
|
||||||
_komanda: {
|
_komanda: {
|
||||||
records: {
|
records: {
|
||||||
|
|||||||
@ -2,8 +2,10 @@ import type { Metadata } from 'next';
|
|||||||
import { Inter } from 'next/font/google';
|
import { Inter } from 'next/font/google';
|
||||||
|
|
||||||
import { textControlApi } from '@/shared/language/api/text-control.api';
|
import { textControlApi } from '@/shared/language/api/text-control.api';
|
||||||
|
import { mediaControlApi } from '@/shared/media/api/media-control.api';
|
||||||
import { Providers } from '@/shared/providers/providers';
|
import { Providers } from '@/shared/providers/providers';
|
||||||
import { makeStore } from '@/shared/store';
|
import { makeStore } from '@/shared/store';
|
||||||
|
import { MediaItem } from '@/shared/types/media.type';
|
||||||
import { TextItem } from '@/shared/types/text.types';
|
import { TextItem } from '@/shared/types/text.types';
|
||||||
|
|
||||||
import { Footer } from '@/widgets/footer';
|
import { Footer } from '@/widgets/footer';
|
||||||
@ -29,10 +31,15 @@ export default async function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
const store = makeStore();
|
const store = makeStore();
|
||||||
|
|
||||||
const response = await store.dispatch(
|
// Запрос текстов
|
||||||
|
const textResponse = await store.dispatch(
|
||||||
textControlApi.endpoints.fetchText.initiate(),
|
textControlApi.endpoints.fetchText.initiate(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Запрос медиа
|
||||||
|
const mediaResponse = await store.dispatch(
|
||||||
|
mediaControlApi.endpoints.fetchMedia.initiate(),
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<html
|
<html
|
||||||
lang='ru'
|
lang='ru'
|
||||||
@ -41,7 +48,10 @@ export default async function RootLayout({
|
|||||||
style={{ scrollBehavior: 'smooth' }}
|
style={{ scrollBehavior: 'smooth' }}
|
||||||
>
|
>
|
||||||
<body className={`${inter.className} min-w-2xs antialiased`}>
|
<body className={`${inter.className} min-w-2xs antialiased`}>
|
||||||
<Providers textItems={response.data as TextItem[]}>
|
<Providers
|
||||||
|
textItems={textResponse.data as TextItem[]}
|
||||||
|
mediaItems={mediaResponse.data as MediaItem[]}
|
||||||
|
>
|
||||||
<Header />
|
<Header />
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import { TextControlContext } from '../context/text-control-provider';
|
|||||||
export function useTextController() {
|
export function useTextController() {
|
||||||
const context = useContext(TextControlContext);
|
const context = useContext(TextControlContext);
|
||||||
if (context === undefined) {
|
if (context === undefined) {
|
||||||
throw new Error('useLanguage must be used within a LanguageProvider');
|
throw new Error(
|
||||||
|
'useTextController must be used within a TextControlProvider',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof context.t !== 'function') {
|
if (typeof context.t !== 'function') {
|
||||||
|
|||||||
24
src/shared/media/api/media-control.api.ts
Normal file
24
src/shared/media/api/media-control.api.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { jsonToGraphQLQuery } from 'json-to-graphql-query';
|
||||||
|
|
||||||
|
import { presentMedia } from '@/app/api-utlities/presenters';
|
||||||
|
import { mediaRequest } from '@/app/api-utlities/requests/common';
|
||||||
|
|
||||||
|
import { taylorAPI } from '@/shared/api/taylor-api';
|
||||||
|
import { MediaItem } from '@/shared/types/media.type';
|
||||||
|
|
||||||
|
export const mediaControlApi = taylorAPI.injectEndpoints({
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
fetchMedia: builder.query<MediaItem[], void>({
|
||||||
|
query: () => ({
|
||||||
|
url: '',
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
query: jsonToGraphQLQuery({ query: mediaRequest }),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
transformResponse: (response: any) => {
|
||||||
|
return presentMedia(response.data._mediaKontent);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
42
src/shared/media/context/media-control.provider.tsx
Normal file
42
src/shared/media/context/media-control.provider.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { createContext, type ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { MediaItem } from '@/shared/types/media.type';
|
||||||
|
|
||||||
|
export type MediaMap = Record<string, MediaItem>;
|
||||||
|
|
||||||
|
type MediaControlContextType = {
|
||||||
|
m: (key: string) => MediaItem | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MediaControlContext = createContext<
|
||||||
|
MediaControlContextType | undefined
|
||||||
|
>(undefined);
|
||||||
|
|
||||||
|
export function MediaControlProvider({
|
||||||
|
children,
|
||||||
|
mediaItems,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
mediaItems?: MediaItem[];
|
||||||
|
}) {
|
||||||
|
const mediaMap = mediaItems?.reduce((pr, cr) => {
|
||||||
|
pr[cr.key] = cr;
|
||||||
|
return pr;
|
||||||
|
}, {} as MediaMap);
|
||||||
|
|
||||||
|
const getMedia = (key: string): MediaItem | undefined => {
|
||||||
|
if (mediaMap?.[key]) {
|
||||||
|
return mediaMap[key];
|
||||||
|
}
|
||||||
|
console.warn(`Media key not found: ${key}`);
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MediaControlContext.Provider value={{ m: getMedia }}>
|
||||||
|
{children}
|
||||||
|
</MediaControlContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
20
src/shared/media/hooks/use-media-controller.ts
Normal file
20
src/shared/media/hooks/use-media-controller.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
import { MediaControlContext } from '../context/media-control.provider';
|
||||||
|
|
||||||
|
export function useMediaController() {
|
||||||
|
const context = useContext(MediaControlContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
'useMediaController must be used within a MediaControlProvider',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof context.m !== 'function') {
|
||||||
|
throw new Error('Media function (m) is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
@ -4,8 +4,10 @@ import { TooltipProvider } from '@radix-ui/react-tooltip';
|
|||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
import { TextControlProvider } from '../language';
|
import { TextControlProvider } from '../language';
|
||||||
|
import { MediaControlProvider } from '../media/context/media-control.provider';
|
||||||
import { store } from '../store';
|
import { store } from '../store';
|
||||||
import { ThemeProvider } from '../theme/theme-provider';
|
import { ThemeProvider } from '../theme/theme-provider';
|
||||||
|
import { MediaItem } from '../types/media.type';
|
||||||
import { TextItem } from '../types/text.types';
|
import { TextItem } from '../types/text.types';
|
||||||
import { AosProvider } from './aos-provider';
|
import { AosProvider } from './aos-provider';
|
||||||
import { Toaster } from './toaster';
|
import { Toaster } from './toaster';
|
||||||
@ -13,25 +15,32 @@ import { Toaster } from './toaster';
|
|||||||
type ProvidersProps = {
|
type ProvidersProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
textItems: TextItem[];
|
textItems: TextItem[];
|
||||||
|
mediaItems: MediaItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Providers = ({ children, textItems }: ProvidersProps) => {
|
export const Providers = ({
|
||||||
|
children,
|
||||||
|
textItems,
|
||||||
|
mediaItems,
|
||||||
|
}: ProvidersProps) => {
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<TextControlProvider textItems={textItems}>
|
<TextControlProvider textItems={textItems}>
|
||||||
<ThemeProvider
|
<MediaControlProvider>
|
||||||
attribute='class'
|
<ThemeProvider
|
||||||
defaultTheme='light'
|
attribute='class'
|
||||||
enableSystem
|
defaultTheme='light'
|
||||||
disableTransitionOnChange
|
enableSystem
|
||||||
>
|
disableTransitionOnChange
|
||||||
<TooltipProvider>
|
>
|
||||||
<AosProvider>
|
<TooltipProvider>
|
||||||
{children}
|
<AosProvider>
|
||||||
<Toaster />
|
{children}
|
||||||
</AosProvider>
|
<Toaster />
|
||||||
</TooltipProvider>
|
</AosProvider>
|
||||||
</ThemeProvider>
|
</TooltipProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</MediaControlProvider>
|
||||||
</TextControlProvider>
|
</TextControlProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
8
src/shared/types/media.type.ts
Normal file
8
src/shared/types/media.type.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface MediaItem {
|
||||||
|
key: string; // _klyuchNeIzmenya
|
||||||
|
name: string; // _name
|
||||||
|
photo: {
|
||||||
|
id: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@ import { GasStationMap } from '@/features/map';
|
|||||||
|
|
||||||
import { Container } from '@/shared/components/container';
|
import { Container } from '@/shared/components/container';
|
||||||
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
import { useTextController } from '@/shared/language/hooks/use-text-controller';
|
||||||
|
import { useMediaController } from '@/shared/media/hooks/use-media-controller';
|
||||||
|
|
||||||
interface MapSectionProps {
|
interface MapSectionProps {
|
||||||
stations: Stations;
|
stations: Stations;
|
||||||
@ -16,6 +17,11 @@ interface MapSectionProps {
|
|||||||
export const MapSection = ({ stations }: MapSectionProps) => {
|
export const MapSection = ({ stations }: MapSectionProps) => {
|
||||||
const { t } = useTextController();
|
const { t } = useTextController();
|
||||||
|
|
||||||
|
const { m } = useMediaController();
|
||||||
|
|
||||||
|
// NOTE: doesn't work
|
||||||
|
const stationsMedia = m('stations.main');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id='stations' className='bg-gray-50'>
|
<section id='stations' className='bg-gray-50'>
|
||||||
<Container>
|
<Container>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user