master
Netcup Gituser 2023-12-16 09:38:51 +01:00
parent 91df8a53bb
commit 486ef51863
19 changed files with 424 additions and 88 deletions

View File

@ -4,7 +4,7 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
interface MyIconProps { interface MyIconProps {
name: string; name: string;
size?: number | undefined; size?: number | undefined;
color?: number | ColorValue | undefined color?: number | ColorValue | undefined;
} }
export function MyIcon({name, size, color}: MyIconProps) { export function MyIcon({name, size, color}: MyIconProps) {

View File

@ -3,6 +3,11 @@ import {Image, ImageProps} from 'react-native';
type imageType = Blob | undefined; type imageType = Blob | undefined;
export interface BasicImageProp {
source: SourceProp;
url?: string;
data?: imageType;
}
interface CustomImageProps extends ImageProps { interface CustomImageProps extends ImageProps {
hq?: {source: SourceProp; data: imageType; url: string}; hq?: {source: SourceProp; data: imageType; url: string};
} }
@ -11,4 +16,20 @@ function MyImage({...rest}: CustomImageProps) {
return <Image {...rest} />; return <Image {...rest} />;
} }
export function getImageURL(
image: (BasicImageProp | undefined)[],
): string | undefined {
for (let i = 0; i < image.length; i++) {
const img = image[i];
if (img === undefined) continue;
if (img.source === SourceProp.online) {
if (img.url !== undefined) return img.url;
}
}
return undefined;
}
export default MyImage; export default MyImage;

View File

@ -2,12 +2,5 @@ import {Suspense} from 'react';
import {Text} from 'react-native'; import {Text} from 'react-native';
export function MySuspenseFallback({children}: {children: React.ReactNode}) { export function MySuspenseFallback({children}: {children: React.ReactNode}) {
console.log('MySuspenseFallback'); return <Suspense fallback={<Text>loading...</Text>}>{children}</Suspense>;
return (
<Suspense
fallback={<Text style={{color: 'red', fontSize: 64}}>loading...</Text>}>
{children}
</Suspense>
);
} }

View File

@ -4,10 +4,10 @@ import {appStatus, non_save_vars} from './appNonSaveVar';
import {ThemeTokensType} from '@configs/colors'; import {ThemeTokensType} from '@configs/colors';
import {chatEntity, roomId} from '@configs/chat/types'; import {chatEntity, roomId} from '@configs/chat/types';
import {User} from '@user/types'; import {SourceProp, User} from '@user/types';
import {AccountName, EventId} from './types'; import {AccountName, EventId} from './types';
import {PA_Point} from '@components/map/types'; import {PA_Point} from '@components/map/types';
import {PAEvent} from '@event/types'; import {PAEvent, createEventProp} from '@event/types';
export const appNonSaveVariablesSlice = createSlice({ export const appNonSaveVariablesSlice = createSlice({
name: 'non_save_vars', name: 'non_save_vars',
@ -31,7 +31,10 @@ export const appNonSaveVariablesSlice = createSlice({
removeCachedEvent: (state, action: PayloadAction<EventId>) => { removeCachedEvent: (state, action: PayloadAction<EventId>) => {
delete state.cachedEvents[action.payload]; delete state.cachedEvents[action.payload];
}, },
setJoinedEvent: (state, action: PayloadAction<{id: EventId, isJoined: number}>) => {
state.cachedEvents[action.payload.id].isJoined = createEventProp(SourceProp.cached, action.payload.isJoined);
},
setSelectedChat: (state, action: PayloadAction<roomId>) => { setSelectedChat: (state, action: PayloadAction<roomId>) => {
state.selectedChat = action.payload; state.selectedChat = action.payload;
}, },

View File

@ -17,6 +17,8 @@ import {
} from './types'; } from './types';
import {SourceProp} from '@user/types'; import {SourceProp} from '@user/types';
import { appVarActions } from '@configs/appVarReducer';
import { get } from '@gluestack-style/react';
let cachedEventList: EventID[] = []; let cachedEventList: EventID[] = [];
@ -117,6 +119,12 @@ async function getEvent(
), ),
lastUpdateTimestamp: eve[eveDBKeys.lastUpdateTimestamp], lastUpdateTimestamp: eve[eveDBKeys.lastUpdateTimestamp],
EventBannerPicture, EventBannerPicture,
isJoined: createEventProp(SourceProp.offline, eve[eveDBKeys.isJoined]),
minAge: createEventProp(SourceProp.offline, eve[eveDBKeys.minAge]),
AddressText: createEventProp(
SourceProp.offline,
eve[eveDBKeys.AddressText],
),
}; };
} }
} }
@ -153,23 +161,10 @@ async function getEvent(
const resp = { const resp = {
response: { response: {
Name: 'test event', Name: 'Bootshaus',
Description: Description:
'das Event "Tech House Germany" am 08.12.2023 im legendären Bootshaus Köln verspricht eine unvergessliche Nacht voller elektrisierender Beats und pulsierender Rhythmen. Jeder DJ bringt seinen einzigartigen Stil und Energie in das Line-up ein, was eine perfekte Mischung aus klassischem und modernem Tech House garantiert. \n\n' + 'das Event "Tech House Germany" am 08.12.2023 im legendären Bootshaus Köln verspricht eine unvergessliche Nacht voller elektrisierender Beats und pulsierender Rhythmen. Jeder DJ bringt seinen einzigartigen Stil und Energie in das Line-up ein, was eine perfekte Mischung aus klassischem und modernem Tech House garantiert. \n\n' +
'Das Bootshaus, bekannt für seine hervorragende Akustik und beeindruckende Lichtshow, bietet die perfekte Kulisse für eine Nacht voller Tanz und Musik. Erleben Sie eine Reise durch die Welt des Tech House, begleitet von den besten Künstlern der Szene. \n\n' + 'Das Bootshaus, bekannt für seine hervorragende Akustik und beeindruckende Lichtshow, bietet die perfekte Kulisse für eine Nacht voller Tanz und Musik. Erleben Sie eine Reise durch die Welt des Tech House, begleitet von den besten Künstlern der Szene. \n\n' +
'\n\n\n' +
'das Event "Tech House Germany" am 08.12.2023 im legendären Bootshaus Köln verspricht eine unvergessliche Nacht voller elektrisierender Beats und pulsierender Rhythmen. Jeder DJ bringt seinen einzigartigen Stil und Energie in das Line-up ein, was eine perfekte Mischung aus klassischem und modernem Tech House garantiert. \n\n' +
'Das Bootshaus, bekannt für seine hervorragende Akustik und beeindruckende Lichtshow, bietet die perfekte Kulisse für eine Nacht voller Tanz und Musik. Erleben Sie eine Reise durch die Welt des Tech House, begleitet von den besten Künstlern der Szene. \n\n' +
'\n\n\n' +
'das Event "Tech House Germany" am 08.12.2023 im legendären Bootshaus Köln verspricht eine unvergessliche Nacht voller elektrisierender Beats und pulsierender Rhythmen. Jeder DJ bringt seinen einzigartigen Stil und Energie in das Line-up ein, was eine perfekte Mischung aus klassischem und modernem Tech House garantiert. \n\n' +
'Das Bootshaus, bekannt für seine hervorragende Akustik und beeindruckende Lichtshow, bietet die perfekte Kulisse für eine Nacht voller Tanz und Musik. Erleben Sie eine Reise durch die Welt des Tech House, begleitet von den besten Künstlern der Szene. \n\n' +
'\n\n\n' +
'das Event "Tech House Germany" am 08.12.2023 im legendären Bootshaus Köln verspricht eine unvergessliche Nacht voller elektrisierender Beats und pulsierender Rhythmen. Jeder DJ bringt seinen einzigartigen Stil und Energie in das Line-up ein, was eine perfekte Mischung aus klassischem und modernem Tech House garantiert. \n\n' +
'Das Bootshaus, bekannt für seine hervorragende Akustik und beeindruckende Lichtshow, bietet die perfekte Kulisse für eine Nacht voller Tanz und Musik. Erleben Sie eine Reise durch die Welt des Tech House, begleitet von den besten Künstlern der Szene. \n\n' +
'\n\n\n' +
'das Event "Tech House Germany" am 08.12.2023 im legendären Bootshaus Köln verspricht eine unvergessliche Nacht voller elektrisierender Beats und pulsierender Rhythmen. Jeder DJ bringt seinen einzigartigen Stil und Energie in das Line-up ein, was eine perfekte Mischung aus klassischem und modernem Tech House garantiert. \n\n' +
'Das Bootshaus, bekannt für seine hervorragende Akustik und beeindruckende Lichtshow, bietet die perfekte Kulisse für eine Nacht voller Tanz und Musik. Erleben Sie eine Reise durch die Welt des Tech House, begleitet von den besten Künstlern der Szene. \n\n' +
'\n\n\n' +
'Blckbx: \n' + 'Blckbx: \n' +
'NIGHTFUNK \n' + 'NIGHTFUNK \n' +
'FINIQ \n' + 'FINIQ \n' +
@ -183,10 +178,13 @@ async function getEvent(
Type: 0, Type: 0,
Theme: 0, Theme: 0,
FriendList: [], FriendList: [],
UserLength: 5, UserLength: 2121,
pic: 'https://www.w3schools.com/w3css/img_lights.jpg', pic: 'https://res.cloudinary.com/coin-nft/image/upload/c_limit,q_auto,w_329/f_auto/v1/cache/1/f2/65/f26573682335fe72b50f0f74041736905308345d6043abbac832856b3a4d2246-ZGMwNDMxZTktYTY4NS00OTQ1LWJiYmUtMDRhY2Q0YzUzMjAy?_a=ATCkFAA0',
ButtonAction: ButtonAction:
'{"url":"https://www.w3schools.com/w3css/img_lights.jpg"}', '{"url":"https://res.cloudinary.com/coin-nft/image/upload/c_limit,q_auto,w_329/f_auto/v1/cache/1/f2/65/f26573682335fe72b50f0f74041736905308345d6043abbac832856b3a4d2246-ZGMwNDMxZTktYTY4NS00OTQ1LWJiYmUtMDRhY2Q0YzUzMjAy?_a=ATCkFAA0"}',
minAge: 18,
AddressText: 'Bootshaus / Auenweg 173 / 51063 Cologne',
}, },
}; };
@ -227,6 +225,12 @@ async function getEvent(
SourceProp.cached, SourceProp.cached,
resp.response.ButtonAction, resp.response.ButtonAction,
), ),
minAge: createEventProp(SourceProp.cached, resp.response.minAge),
AddressText: createEventProp(
SourceProp.cached,
resp.response.AddressText,
),
isJoined: createEventProp(SourceProp.cached, 0),
}; };
//BigDataManager.setEntry('events', event); //BigDataManager.setEntry('events', event);
@ -329,13 +333,48 @@ function initUndefinedEvent(UUID: EventID): PAEvent {
hq: createEventProp(SourceProp.online), hq: createEventProp(SourceProp.online),
}, },
ButtonAction: createEventProp(SourceProp.online), ButtonAction: createEventProp(SourceProp.online),
isJoined: createEventProp(SourceProp.online),
minAge: createEventProp(SourceProp.online),
AddressText: createEventProp(SourceProp.online),
}; };
} }
function joinEvent(UUID: EventID): Promise<boolean> {
return new Promise((resolve, reject) => {
setTimeout(() => {
store.dispatch(appNonSaveVarActions.setJoinedEvent({id:UUID, isJoined: 1}));
getEvent(UUID, true).then(() => {
resolve(true);
}).catch(() => {
reject(false);
});
},1000);
});
}
function quitEvent(UUID: EventID): Promise<boolean> {
return new Promise((resolve, reject) => {
setTimeout(() => {
store.dispatch(appNonSaveVarActions.setJoinedEvent({id:UUID, isJoined: 0}));
resolve(true);
},1000);
});
}
const EventManager = { const EventManager = {
getEvent, getEvent,
getEventSelector, getEventSelector,
getEventSelectorPicture, getEventSelectorPicture,
initUndefinedEvent, initUndefinedEvent,
joinEvent,
quitEvent,
}; };
export default EventManager; export default EventManager;

View File

@ -46,9 +46,9 @@ export interface PAEvent {
UserLength: BasicEventProp<number>; UserLength: BasicEventProp<number>;
EventBannerPicture: EventBannerPicture; EventBannerPicture: EventBannerPicture;
ButtonAction: BasicEventProp<string>; ButtonAction: BasicEventProp<string>;
//isJoined isJoined: BasicEventProp<number>;
//minAge minAge: BasicEventProp<number>;
//AddressText AddressText: BasicEventProp<string>;
} }
export function NumToEventType(num: number): EventType { export function NumToEventType(num: number): EventType {

View File

@ -19,7 +19,7 @@ export const apiPath = {
export enum apiBackendRequest { export enum apiBackendRequest {
LOGIN = '/user/login', LOGIN = '/user/login',
APP_START = '/appstart', APP_START = '/user',
GET_USER_PROFILE = '/users/:accountName', GET_USER_PROFILE = '/users/:accountName',
LOGOUT = '/user/logout', LOGOUT = '/user/logout',
SIGN_UP = '/user/signup', SIGN_UP = '/user/signup',
@ -29,8 +29,7 @@ export enum apiBackendRequest {
REGISTER_STEP_2 = '/verify/email/:xToken/:verifyId', REGISTER_STEP_2 = '/verify/email/:xToken/:verifyId',
REGISTER_STEP_FINAL = '/admin/users', REGISTER_STEP_FINAL = '/admin/users',
REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK = '/admin/users/validation/accountname', REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK = '/admin/users/validation/accountname',
LOGIN = '/user', LOGIN = '/user', */
APP_START = '/appstart', */
} }
type requestGET = {[key: string]: string}; type requestGET = {[key: string]: string};
@ -161,6 +160,8 @@ interface APP_START extends defaultRequest {
path: apiBackendRequest.APP_START; path: apiBackendRequest.APP_START;
response: { response: {
accountName: AccountName;
username: Username;
/* TokenValid: boolean; /* TokenValid: boolean;
AccountName: AccountName; AccountName: AccountName;
Username: Username; Username: Username;

View File

@ -21,6 +21,9 @@ enum keys {
EventBannerPictureBinaryLQ = 'n', EventBannerPictureBinaryLQ = 'n',
EventBannerPictureBinaryHQ = 'o', EventBannerPictureBinaryHQ = 'o',
ButtonAction = 'p', ButtonAction = 'p',
isJoined = 'q',
minAge = 'r',
AddressText = 's',
} }
const name = 'events'; const name = 'events';
@ -43,6 +46,9 @@ const propsType: {[key in keyof typeof propsDefault]: string} = {
[keys.EventBannerPictureBinaryLQ]: 'data', [keys.EventBannerPictureBinaryLQ]: 'data',
[keys.EventBannerPictureBinaryHQ]: 'data', [keys.EventBannerPictureBinaryHQ]: 'data',
[keys.ButtonAction]: 'string', [keys.ButtonAction]: 'string',
[keys.isJoined]: 'int',
[keys.minAge]: 'int',
[keys.AddressText]: 'string',
}; };
const propsDefault = { const propsDefault = {
@ -62,6 +68,9 @@ const propsDefault = {
[keys.EventBannerPictureBinaryLQ]: new ArrayBuffer(0), [keys.EventBannerPictureBinaryLQ]: new ArrayBuffer(0),
[keys.EventBannerPictureBinaryHQ]: new ArrayBuffer(0), [keys.EventBannerPictureBinaryHQ]: new ArrayBuffer(0),
[keys.ButtonAction]: '', [keys.ButtonAction]: '',
[keys.isJoined]: 0,
[keys.minAge]: 0,
[keys.AddressText]: '',
}; };
const thisSchema: databaseConf<typeof propsDefault, typeof keys> = { const thisSchema: databaseConf<typeof propsDefault, typeof keys> = {

View File

@ -6,6 +6,13 @@ interface LangDetails {
export default interface LangFormat { export default interface LangFormat {
details: LangDetails; details: LangDetails;
appName: string; appName: string;
event: {
info: string;
details: string;
tickets: string;
join: string;
quit: string;
};
navigation: { navigation: {
home: { home: {
profile: { profile: {

View File

@ -5,6 +5,13 @@ export const lang: LangFormat = {
langCode: 'en', langCode: 'en',
langName: 'English', langName: 'English',
}, },
event: {
info: 'INFO',
details: 'DETAILS',
tickets: 'Tickets',
join: 'Join',
quit: 'Quit',
},
appName: 'Party App', appName: 'Party App',
navigation: { navigation: {
home: { home: {

View File

@ -16,7 +16,7 @@ import ProfileTab, {
ProfileStackNavigatorParamList, ProfileStackNavigatorParamList,
} from './tabs/main/ProfileTab'; } from './tabs/main/ProfileTab';
import {FadeInView} from '@helper/animations'; import {FadeInView} from '@helper/animations';
import {Animated, View} from 'react-native'; import {Animated, AppState, View} from 'react-native';
import LinearGradient from 'react-native-linear-gradient'; import LinearGradient from 'react-native-linear-gradient';
import { import {
RegistrationScreenAnim, RegistrationScreenAnim,
@ -30,6 +30,7 @@ import {Text} from '@gluestack-ui/themed';
import {MyTouchableOpacity} from '@components/MyTouchableOpacity'; import {MyTouchableOpacity} from '@components/MyTouchableOpacity';
import {useEffect, useRef} from 'react'; import {useEffect, useRef} from 'react';
import {animated, useSpring} from '@react-spring/native'; import {animated, useSpring} from '@react-spring/native';
import {apiBackendRequest, makeRequest} from '@helper/request';
export type RootStackNavigatorParamList = { export type RootStackNavigatorParamList = {
Home: NavigatorScreenParams<HomeStackNavigatorParamList>; Home: NavigatorScreenParams<HomeStackNavigatorParamList>;
@ -43,6 +44,32 @@ export default function Navigation() {
const currentUser = const currentUser =
reduxStore.getState().appVariables.preferences.selectedAccount; reduxStore.getState().appVariables.preferences.selectedAccount;
useEffect(() => {
console.log('APP NAVIGATION');
makeRequest({
path: apiBackendRequest.APP_START,
response: {
accountName: '',
username: '',
/*
AccountName: '',
Description: '',
FollowersCount: 0,
FollowingCount: 0,
Username: '',
XpLevel: 0,
XpPoints: 0,
AvatarUrl: '',
AccountStatus: 0,
Events: {},
TokenValid: false, */
},
}).then(response => {
console.log(response);
});
}, []);
return ( return (
<Stack.Navigator <Stack.Navigator
screenOptions={{ screenOptions={{
@ -184,7 +211,7 @@ function CustomTabBar(props: BottomTabBarProps) {
borderTopLeftRadius: 20, borderTopLeftRadius: 20,
borderTopRightRadius: 20, borderTopRightRadius: 20,
zIndex: 100, zIndex: 100,
position: 'absolute', position: routeName === 'Calendar' ? 'relative' : 'absolute',
bottom: 0, bottom: 0,
borderColor: currentTheme.backgroundDark300, borderColor: currentTheme.backgroundDark300,
borderWidth: 3, borderWidth: 3,

View File

@ -50,7 +50,7 @@ function MapTab() {
name="Event" name="Event"
options={{ options={{
animation: 'slide_from_right', animation: 'slide_from_right',
title: 'lang', title: '',
headerShown: true, headerShown: true,
headerStyle: headerStyle, headerStyle: headerStyle,
headerShadowVisible: false, headerShadowVisible: false,

View File

@ -32,6 +32,10 @@ const LazyHelp = lazy(() =>
import('@pages/profile/help').then(module => ({default: module.Help})), import('@pages/profile/help').then(module => ({default: module.Help})),
); );
const LazyAbout = lazy(() =>
import('@pages/profile/about').then(module => ({default: module.About})),
);
const Help = () => { const Help = () => {
return ( return (
<MySuspenseFallback> <MySuspenseFallback>
@ -40,10 +44,6 @@ const Help = () => {
); );
}; };
const LazyAbout = lazy(() =>
import('@pages/profile/about').then(module => ({default: module.About})),
);
const About = () => { const About = () => {
return ( return (
<MySuspenseFallback> <MySuspenseFallback>

View File

@ -1,19 +1,58 @@
import {MyScreenContainer} from '@components/MyScreenContainer'; import {MyTouchableOpacity} from '@components/MyTouchableOpacity';
import {Text} from '@gluestack-ui/themed'; import {Text} from '@gluestack-ui/themed';
import {RootState} from '@redux/store'; import {RootState} from '@redux/store';
import {Image} from 'react-native';
import {FlatList} from 'react-native';
import {View} from 'react-native'; import {View} from 'react-native';
import {useSelector} from 'react-redux'; import {useSelector} from 'react-redux';
const baseUrls = [
'https://www.w3schools.com/w3css/img_lights.jpg',
'https://suburbanmen.com/wp-content/uploads/2021/12/instagram-crush-emilia-bte-20211202-125.jpg',
'https://i.ytimg.com/vi/X0I6PbIWmPo/hqdefault.jpg',
'https://i.ytimg.com/vi/RDrJ1VmRi0w/hqdefault.jpg',
];
const events: ArrayLike<any> | null | undefined = [];
for (let i = 1; i <= 100; i++) {
const randomUrlIndex = Math.floor(Math.random() * baseUrls.length);
events.push({
id: i,
url: baseUrls[randomUrlIndex],
title: 'Bootshaus',
description: '01.12.2023 20:00 Uhr',
});
}
/*
const events = [ const events = [
{ {
id: 1,
url: 'https://www.w3schools.com/w3css/img_lights.jpg',
title: 'Bootshaus', title: 'Bootshaus',
description: '01.12.2023 20:00 Uhr', description: '01.12.2023 20:00 Uhr',
}, },
{ {
id: 2,
url: 'https://suburbanmen.com/wp-content/uploads/2021/12/instagram-crush-emilia-bte-20211202-125.jpg',
title: 'Bootshaus', title: 'Bootshaus',
description: '01.12.2023 20:00 Uhr', description: '01.12.2023 20:00 Uhr',
}, },
]; {
id: 3,
url: 'https://i.ytimg.com/vi/X0I6PbIWmPo/hqdefault.jpg',
title: 'Bootshaus',
description: '01.12.2023 20:00 Uhr',
},
{
id: 4,
url: 'https://i.ytimg.com/vi/RDrJ1VmRi0w/hqdefault.jpg',
title: 'Bootshaus',
description: '01.12.2023 20:00 Uhr',
},
];*/
export function CalendarScreen() { export function CalendarScreen() {
const currentTheme = useSelector( const currentTheme = useSelector(
@ -21,8 +60,41 @@ export function CalendarScreen() {
); );
return ( return (
<MyScreenContainer> <View>
<Text>Calendar</Text> <FlatList
</MyScreenContainer> data={events}
keyExtractor={(item, index) => `${item.id}${index}`}
renderItem={({item}) => (
<MyTouchableOpacity
key={item.id}
style={{
margin: 12,
padding: 12,
borderRadius: 10,
backgroundColor: currentTheme.backgroundDark300,
marginBottom: 12,
flexDirection: 'row',
gap: 12,
}}>
<View style={{backgroundColor: currentTheme.backgroundDark200}}>
<Image
style={{
width: 100,
height: 60,
}}
source={{uri: item.url}}
/>
</View>
<View>
<Text size="xl" bold>
{item.title}
</Text>
<Text>{item.description}</Text>
</View>
</MyTouchableOpacity>
)}
/>
</View>
); );
} }

View File

@ -1,12 +1,16 @@
import {Center, Spinner, Text, View} from '@gluestack-ui/themed'; import {Box, Center, Spinner, Text, View} from '@gluestack-ui/themed';
import {EventID} from '@event/types'; import {EventID} from '@event/types';
import EventManager from '@event/EventManager'; import EventManager from '@event/EventManager';
import {MyScreenContainer} from '@components/MyScreenContainer'; import {MyScreenContainer} from '@components/MyScreenContainer';
import {SourceProp} from '@user/types'; import {SourceProp} from '@user/types';
import MyImage from '@components/MyImage'; import MyImage, {getImageURL} from '@components/MyImage';
import {Image} from 'react-native'; import {Image, Linking} from 'react-native';
import {useEffect, useState} from 'react';
import {useSelector} from 'react-redux';
import {RootState} from '@redux/store';
import {MyButton} from '@components/MyButton';
function EventPageLoading() { function EventPageLoading() {
// center also horizontally and vertically // center also horizontally and vertically
@ -17,48 +21,138 @@ function EventPageLoading() {
); );
} }
function EventPage({route}: {route: {params: {eventID: EventID}}}) { function EventPage({
route,
navigation,
}: {
route: {params: {eventID: EventID}};
navigation: any;
}) {
const {eventID} = route.params; const {eventID} = route.params;
const event = EventManager.getEventSelector(eventID); const event = EventManager.getEventSelector(eventID);
if (event.Name.source === SourceProp.online) return <EventPageLoading />; const lang = useSelector((state: RootState) => state.appVariables.lang.event);
let image = event.EventBannerPicture.hq; const [isJoining, setIsJoining] = useState(false); // for join button (loading)
if (image === undefined) {
image = event.EventBannerPicture.lq;
}
const imageUrl =
'https://res.cloudinary.com/coin-nft/image/upload/c_limit,q_auto,w_329/f_auto/v1/cache/1/f2/65/f26573682335fe72b50f0f74041736905308345d6043abbac832856b3a4d2246-ZGMwNDMxZTktYTY4NS00OTQ1LWJiYmUtMDRhY2Q0YzUzMjAy?_a=ATCkFAA0';
useEffect(() => { useEffect(() => {
// Fetch the image dimensions and calculate the aspect ratio navigation.setOptions({
Image.getSize( title: event.Name.data,
imageUrl, });
(width, height) => { }, [event.Name.data]);
const calculatedAspectRatio = width / height;
setAspectRatio(calculatedAspectRatio); if (event.Name.source === SourceProp.online) return <EventPageLoading />;
},
error => { const imageUrl = getImageURL([
console.error('Error getting image size: ', error); event.EventBannerPicture.hq,
}, event.EventBannerPicture.lq,
); ]);
}, [imageUrl]);
const isJoinedToEvent = event.isJoined.data === 1;
//const imageUrl = 'https://res.cloudinary.com/coin-nft/image/upload/c_limit,q_auto,w_329/f_auto/v1/cache/1/f2/65/f26573682335fe72b50f0f74041736905308345d6043abbac832856b3a4d2246-ZGMwNDMxZTktYTY4NS00OTQ1LWJiYmUtMDRhY2Q0YzUzMjAy?_a=ATCkFAA0';
return ( return (
<MyScreenContainer scrollView={true}> <MyScreenContainer scrollView={true}>
{ {
<> <>
<Text>{event.Name.data}</Text> <Box
<Image marginBottom={20}
source={{ style={{
uri: imageUrl, maxWidth: '100%',
}} width: 444,
style={{width: '100%', height: 400}} }}>
alt="Event banner" <MyImage
/> source={{
<Text>{event.Description.data}</Text> uri: imageUrl,
}}
style={{
width: '100%',
height: 250,
borderRadius: 10,
}}
alt="Event banner"
/>
<Text marginTop={10} fontWeight={'100'} color="$textLight400">
{lang.info}
</Text>
<Box
padding={10}
backgroundColor="$backgroundDark300"
borderRadius={10}>
<Text>{event.Description.data}</Text>
</Box>
<Text marginTop={10} fontWeight={'100'} color="$textLight400">
{lang.details}
</Text>
<Box
padding={10}
backgroundColor="$backgroundDark300"
borderRadius={10}>
<Text color="$textLight400">{event.UserLength.data}</Text>
<Text color="$textLight400">{event.minAge.data}</Text>
<Text color="$textLight400">{event.AddressText.data}</Text>
</Box>
<MyButton
type="secondary"
style={{marginTop: 20}}
text={lang.tickets}
onPress={() => {
// open link in browser
if (event.ButtonAction.data === undefined) return;
{
const button = JSON.parse(event.ButtonAction.data);
if (button.url !== undefined) Linking.openURL(button.url);
}
}}
/>
<MyButton
type={isJoinedToEvent ? 'secondary' : 'primary'}
style={{marginTop: 5}}
isLoading={isJoining}
disabled={isJoining}
text={isJoinedToEvent ? lang.quit : lang.join}
onPress={() => {
setIsJoining(true);
if (!isJoinedToEvent) {
EventManager.joinEvent(eventID)
.then(() => {
setIsJoining(false);
})
.catch(() => {
setIsJoining(false);
});
} else {
EventManager.quitEvent(eventID)
.then(() => {
setIsJoining(false);
})
.catch(() => {
setIsJoining(false);
});
}
}}
/>
<Text marginTop={10} fontWeight={'100'} color="$textLight400">
debug
</Text>
<Box
padding={10}
backgroundColor="$backgroundDark300"
borderRadius={10}>
<Text color="$textLight400">{event.StartDate.data}</Text>
<Text color="$textLight400">{event.EndDate.data}</Text>
</Box>
</Box>
</> </>
} }
</MyScreenContainer> </MyScreenContainer>

View File

@ -7,11 +7,18 @@ import DisplayMarkerList from '@components/map/DisplayMarkerList';
import getLocationData from '@components/map/cluster/getData'; import getLocationData from '@components/map/cluster/getData';
import {PA_Point} from '@components/map/types'; import {PA_Point} from '@components/map/types';
import {store} from '@redux/store'; import {RootState, store} from '@redux/store';
import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer'; import {appNonSaveVarActions} from '@configs/appNonSaveVarReducer';
import {Position} from '@rnmapbox/maps/src/types/Position'; import {Position} from '@rnmapbox/maps/src/types/Position';
import {Dimensions} from 'react-native'; import {Dimensions} from 'react-native';
import {MyIconButton} from '@components/MyButton';
import {MyTouchableOpacity} from '@components/MyTouchableOpacity';
import {MyIcon} from '@components/MyIcon';
import {useSelector} from 'react-redux';
import {Button, ButtonIcon} from '@gluestack-ui/themed';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
Mapbox.setAccessToken( Mapbox.setAccessToken(
'pk.eyJ1IjoidGl0YW5pdW1iYWNoIiwiYSI6ImNscGgzZGJxMDAwbHQyaXA2N3BtOWUxbWkifQ.x-f8JJxwQHWmPFI3P6Qn-w', 'pk.eyJ1IjoidGl0YW5pdW1iYWNoIiwiYSI6ImNscGgzZGJxMDAwbHQyaXA2N3BtOWUxbWkifQ.x-f8JJxwQHWmPFI3P6Qn-w',
@ -25,6 +32,10 @@ export const Map = () => {
//const [mapMarkers, setMapMarkers] = useState<PA_Point[]>([]); // Add useState for visibleBounds //const [mapMarkers, setMapMarkers] = useState<PA_Point[]>([]); // Add useState for visibleBounds
const [lastDataRerender, setLastDataRerender] = useState(0); // Add useState for visibleBounds const [lastDataRerender, setLastDataRerender] = useState(0); // Add useState for visibleBounds
const colors = useSelector(
(state: RootState) => state.nonSaveVariables.theme.colors,
);
const getVisibleBounds = async () => { const getVisibleBounds = async () => {
// return when lastDataRerender is 300ms ago // return when lastDataRerender is 300ms ago
const now = Date.now(); const now = Date.now();
@ -124,6 +135,14 @@ export const Map = () => {
projection="globe"> projection="globe">
<DisplayMarkerList /> <DisplayMarkerList />
</Mapbox.MapView> </Mapbox.MapView>
<MapIconButton
MyIconProps={{
name: 'close',
size: 24,
backgroundColor: colors.backgroundDark300,
}}
onPress={() => {}}
/>
</View> </View>
</View> </View>
); );
@ -143,3 +162,30 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
}, },
}); });
function MapIconButton({
onPress,
MyIconProps,
}: {
onPress: () => void;
MyIconProps: {
name: string;
color?: string;
size: number;
backgroundColor?: string;
};
}) {
return (
<View
style={{
position: 'absolute',
top: 60,
right: 10,
width: MyIconProps.size * 2,
height: MyIconProps.size * 2,
}}>
<MaterialIcon.Button name="facebook" backgroundColor="#3b5998" />
</View>
);
}

View File

@ -17,6 +17,8 @@ import {MyTouchableOpacity} from '@components/MyTouchableOpacity';
import UserManager from '@user/UserManager'; import UserManager from '@user/UserManager';
import MyUserManager from '@user/MyUserManager'; import MyUserManager from '@user/MyUserManager';
import {apiBackendRequest, makeRequest} from '@helper/request'; import {apiBackendRequest, makeRequest} from '@helper/request';
import reactStringReplace from 'react-string-replace';
import {passwordOptions, userNameOptions} from '@configs/types';
function UserAvatar() { function UserAvatar() {
return ( return (
@ -262,13 +264,22 @@ export function UpdateUsername() {
); );
const navigation = useNavigation<ProfileScreenNavigationProp>(); const navigation = useNavigation<ProfileScreenNavigationProp>();
// TODO: get username from current logged in user const user = UserManager.getUserSelector(
const username = 'Max Mustermann'; useSelector(
(state: RootState) => state.appVariables.preferences.selectedAccount,
),
);
const [newUsername, setNewUsername] = useState(username); const [newUsername, setNewUsername] = useState(user.Username.data || '');
const info2Text = reactStringReplace(
lang.changeUsername.info2,
'${minLength}',
() => userNameOptions.minLength.toString(),
);
useEffect(() => { useEffect(() => {
const changed = username !== newUsername; const changed = user.Username.data !== newUsername;
navigation.setOptions({ navigation.setOptions({
headerRight: () => headerRight: () =>
@ -299,7 +310,7 @@ export function UpdateUsername() {
<Text color={currentTheme.textLight100} style={{marginTop: 12}}> <Text color={currentTheme.textLight100} style={{marginTop: 12}}>
{lang.changeUsername.info} {lang.changeUsername.info}
</Text> </Text>
<Text color={currentTheme.textLight100}>{lang.changeUsername.info2}</Text> <Text color={currentTheme.textLight100}>{info2Text}</Text>
</MyScreenContainer> </MyScreenContainer>
); );
} }
@ -318,6 +329,10 @@ export function UpdatePassword() {
const [newPassword, setNewPassword] = useState(''); const [newPassword, setNewPassword] = useState('');
const [repeatNewPassword, setRepeatNewPassword] = useState(''); const [repeatNewPassword, setRepeatNewPassword] = useState('');
const infoText = reactStringReplace(lang.info, '${minLength}', () =>
passwordOptions.minLength.toString(),
);
useEffect(() => { useEffect(() => {
const passwordChanged = const passwordChanged =
currentPassword.length > 0 && currentPassword.length > 0 &&
@ -375,7 +390,7 @@ export function UpdatePassword() {
</View> </View>
<Text color={currentTheme.textLight100} style={{marginTop: 12}}> <Text color={currentTheme.textLight100} style={{marginTop: 12}}>
{lang.info} {infoText}
</Text> </Text>
<Text color={currentTheme.textLight100}>{lang.info2}</Text> <Text color={currentTheme.textLight100}>{lang.info2}</Text>
</MyScreenContainer> </MyScreenContainer>

View File

@ -27,7 +27,7 @@ export function Login() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [accountName, setAccountName] = useState('anna'); const [accountName, setAccountName] = useState('anna');
const [password, setPassword] = useState('testtesttest'); const [password, setPassword] = useState('testtesttest1#S');
const loginEnabled = accountName.length > 0 && password.length > 0; const loginEnabled = accountName.length > 0 && password.length > 0;

View File

@ -41,6 +41,8 @@ function createNewMyUser(
path: apiBackendRequest.APP_START, path: apiBackendRequest.APP_START,
requestGET: {':AccountName': AccountName}, requestGET: {':AccountName': AccountName},
response: { response: {
accountName: '',
username: '',
/* /*
AccountName: '', AccountName: '',
Description: '', Description: '',