Giter Site home page Giter Site logo

tamotam-com / tamotam-app Goto Github PK

View Code? Open in Web Editor NEW
34.0 1.0 4.0 2.94 MB

(🧪 Early Beta) 🤙 TamoTam. HangOut. Offline.

Home Page: https://tamotam.com

TypeScript 89.32% JavaScript 0.63% Java 5.15% Ruby 2.67% Objective-C 0.19% Swift 0.06% Objective-C++ 1.98%
android ios react-native react-native-app javascript lifestyle lifestyle-app social-network typescript entertainment

tamotam-app's Introduction

🤙 TamoTam. HangOut. Offline.

TamoTam in Apple App Store TamoTam in Google Play Store

Description

Map with offline events happening around you. You don't need to log in or register.

Go Online to be Offline.

TamoTam aims to limit online time spent on digital applications in favor of offline social life outside the screen.

The application aims to be minimalistic and exclude features such as:

  • Addictive Feed,
  • Destructive Notifications,
  • Brain-affecting Likes,
  • Comments, #StopHate,
  • Share, #StopFakeNews,
  • Other complicated Algorithms unconsciously affect your stay online.

That's because we believe Social Media affects us mentally, such as Anxiety, Depression, Fear Of Missing Out (FOMO), Fear of Speaking Up, Isolation, and more.

Look what's happening around you in Real. Offline. Social Life. Could you add your events (log in or registration required) to contribute to that idea?

Note

The application is in an early stage of development with a number of improvements & issues. It will take time before the application will be complete, performant and user-friendly, but in late 2023 we expect Early Beta.

Thanks in advance for understanding while reviewing TamoTam.

Developers

That's an Open Source project; feel free to contribute.

Frontend/JavaScript/Web Developer interested in React Native/JavaScript Mobile Development?

The project is a combination of a personal education project to learn JavaScript Mobile Development using React Native and business idea to create a mobile app with offline-only events to limit online time.

Therefore, even if that's just from business point of view super easy app for now, it contains several nice technical implementation, which helped me to understand the ecosystem on React Native and become better developer.

Technological stack

  • React/Native + TypeScript;
  • Redux;
  • Different databases (Firebase, SQLite, AsyncStorage);
  • AsyncStorage, which is really comparable to localStorage we have on Web;
  • Caching data using AsyncStorage;
  • Making the Web code actually compiled & working with deployed app to the store;
  • Logging & Monitoring to Firebase Analytics/Firebase Performance Monitoring;
  • Reporting crashes to Firebase Crashlytics;
  • Handling environmental variables in Android, iOS, and Web - changes in native code were required;
  • Integration of 3rd party tools, like Firebase-* and Google Maps;
  • Usage, looks & feel of Material Design (React Native Paper) in a real application.

Stay up to date

Keep yourself up to date about TamoTam and me motivated by giving a Star :-) Star TamoTam on GitHub

Run it

Android Simulator

  1. Run on Android Studio
  2. yarn start
  3. adb reverse tcp:8081 tcp:8081

Alternatively, expo run:android --variant release, for production version.

Kill Android Simulator

adb -s emulator-5554 emu kill, where emulator-5554 is the emulator name.

iOS Simulator

  1. Build using Xcode, if the application isn't installed on the simulator
  2. yarn start
  3. i

Alternatively, expo run:ios --configuration Release, for production version.

Release

  1. eas build -p android
  2. eas build -p ios

Architecture

We're using Redux, but the easiest to understand the architecture is the image below with Flux architecture, which in fact is really similar. However, it's important to note we're using 1 store, like in Redux architecture.

Data Flow Architecture image Image source: https://www.freecodecamp.org/news/an-introduction-to-the-flux-architectural-pattern-674ea74775c9/

External API's, like Ticketmaster, provide +/- 10k of external events. The TamoTam's client is fetching also events added by the user, from Firebase. After all events will be fetched, those are being cached locally, using AsyncStorage. After events are being cached locally on a device, the user can save their favourite events. Those saved events are also saved locally on a device. Those are saved using SQLite. In addition to that, users can add their own events, and those are saved in Firebase.

Application Architecture image Made using: https://app.diagrams.net

In media

Research

Contact

contact[at]tamotam[dot]com

(demo) number of Android downloads badge: https://playbadges.pavi2410.me/badge/full?id=com.tamotam.application

tamotam-app's People

Contributors

danieldanielecki avatar divyesh-rn avatar divyeshgajera1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

tamotam-app's Issues

Add datepicker to the events

It's a crucial feature; for now, users can only perform CRUD operations on description, title, location, and image.

No caching after 7 days

Investigate if TamoTam’s cache stops working after 7 days. It looks like it's the case and after 7 days, the application on each launch requests to fetch all the events without caching it for the next 7 days.

Limit adding events

Only registered and/or paid users should be able to add events.
Currently, everyone without registration can add events.

Sets instead of Arrays for events

Events holding data from externally sourced could use a Set instead of an Array to remove duplicates. It's essential to fix lots of TODO regarding an id, which isn't unique. It'd need to be excluded somehow.

User's events are sometimes added automatically to Cloud Firestore for Firebase

Try to debug it, it might be related to this part of the code:

const [selectedLocation, setSelectedLocation] = useState<Coordinate>({
  latitude: 0,
  longitude: 0,
});

or

let markerCoordinates: Coordinate = { latitude: 0, longitude: 0 };

but it's a bit strange because addEventHandler is fired after <Button />'s onPress(...).

Align then/try/catch/finally

Sometimes it's try {}, catch {}, finally {}, sometimes then(), catch(), finally().
Unify everything to try {}, catch {}, finally {}.

Store images added by users in Cloud Storage for Firebase

Currently, after adding the pictures, the image is visible only to the user who has added this.

However, even for that user, after reloading of fetched events, it will show a no-image.jpeg image. It shouldn't be the case, but we don't save the image anywhere yet. The proposal is to use Cloud Storage for Firebase for storing an image and then map to that image in object for events created by users.

Docs: https://firebase.google.com/docs/storage

Limit maxDate/minDate for events

Currently, the user can add whatever it wants, but it should be somehow limited.
For example:

  • No events in the past;
  • Furthest event in 365 days (1 year) to the future.

Replace PNG icons using SVG files

At the moment it's not possible to use .svg files in the icon prop, reference: #4196. The .png icons are a little bit blurry due to their small size when zoomed in, but their larger size is also problematic because it looks too big.

The only way to use SVG is to use react-native-svg, but it's using an Image, instead of a pin. In combination with the <Callout> it's a bit hectic solution and didn't actually display anything + we have event handlers, too, so it'd overcomplicate too much. New <View> would need to be introduced.
Examples of such solutions:

The .svg files converted using https://www.pngtosvg.com:

icon-map-tamotam-event
icon-map-user-event

.png files in 512 x 512 dimensions:

red
white

User's events from Cloud Firestore for Firebase often aren't loaded

For some reason, usersEvents events from promiseUsersEvents sometimes aren't reflected on the Map. It might be combined with refreshing promiseUsersEventsmore often than once a week. The external events can stay once a week, butusersEvents` should have been refreshed once a day, or have a button to refresh all the events.

Dispatching events per page

Events could've been dispatched per each API's page for performance improvements.

A similar technique has been incorporated in dispatching Ticketmaster events per country: 57a01df. In that case, there are more "refreshes" on the maps while dispatching the events, and those are shown gradually, and the user isn't left with (more) empty maps. For the Ticketmaster events, it was about Europe being completely empty till all events were loaded from APIs. Instead, the maps refreshes per each country-loaded events.

Split up code to smaller pieces

Take a look into store, where actions and dispatchers are in a single file.

However, there's also feedback regarding EditEventScreen on https://github.com/AidOnline01/review-daniel/blob/main/1-huge-files.md. Similarly, other files could split up.

Example:

About the issue

There is a violation of Single Responsibility principle, which creates huge files and reduces maintability/testability/reusability of codebase.

Example

Example from the code

EditEventScreen

import * as Location from "expo-location";
import analytics from "@react-native-firebase/analytics";
import crashlytics from "@react-native-firebase/crashlytics";
import useColorScheme from "../hooks/useColorScheme";
import Colors from "../constants/Colors";
import CustomMapStyles from "../constants/CustomMapStyles";
import DateTimePicker from "@react-native-community/datetimepicker";
import MaterialHeaderButton from "../components/MaterialHeaderButton";
import MapView, { Marker, PROVIDER_GOOGLE } from "react-native-maps";
import SelectImage from "../components/SelectImage";
import React, {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
Dispatch,
MutableRefObject,
SetStateAction,
} from "react";
import StyledText from "../components/StyledText";
import { updateEvent } from "../store/actions/events";
import { useDispatch, useSelector } from "react-redux";
import { useNetInfo, NetInfoState } from "@react-native-community/netinfo";
import {
ActivityIndicator,
Alert,
ColorSchemeName,
Dimensions,
KeyboardAvoidingView,
Platform,
ScrollView,
StyleSheet,
TextInput,
} from "react-native";
import { Button } from "react-native-paper";
import { Coordinate } from "../interfaces/coordinate";
import { Event } from "../interfaces/event";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import { Region } from "../interfaces/region";
import { View } from "../components/Themed";

export default function EditEventScreen({ navigation, route }: any) {
const colorScheme: ColorSchemeName = useColorScheme();
const dispatch: Dispatch<any> = useDispatch<Dispatch<any>>();
const eventId: number = route.params.eventId;
const internetState: NetInfoState = useNetInfo();
const mapRef: MutableRefObject<null> = useRef<null>(null);
const selectedEvent: Event = useSelector<any, any>((state: any) =>
  state.events.savedEvents.find((event: Event) => event.id === eventId)
);
const [dateTimeMode, setDateTimeMode] = useState<string | any>("");
const [descriptionValue, setDescriptionValue] = useState<string>("");
const [error, setError] = useState<Error>(new Error());
const [initialRegionValue, setInitialRegionValue] = useState<Region>({
  latitude: selectedEvent.latitude,
  longitude: selectedEvent.longitude,
  latitudeDelta: 10,
  longitudeDelta: 10,
});
const [isLoading, setIsLoading] = useState<boolean>(false);
const [selectedDate, setSelectedDate] = useState<any | Date>(
  new Date(selectedEvent.date) instanceof Date
    ? new Date(selectedEvent.date)
    : new Date()
);
const [selectedImage, setSelectedImage] = useState<string>("");
const [selectedLocation, setSelectedLocation] = useState<Coordinate>();
const [showDatepicker, setShowDatepicker] = useState<boolean>(false);
const [titleValue, setTitleValue] = useState<string>("");
let markerCoordinates: Coordinate = {
  latitude: selectedEvent.latitude ? selectedEvent.latitude : 0,
  longitude: selectedEvent.longitude ? selectedEvent.longitude : 0,
};

useEffect(() => {
  if (error.message !== "") {
    Alert.alert(
      "Unknown Error ❌",
      "Please report this error by sending an email to us at [email protected]. It will help us 🙏\nError details: " + error.message + "\nDate: " + new Date(),
      [{ text: "Okay" }]
    );
    analytics().logEvent("custom_log", {
      description: "--- Analytics: screens -> EditEventScreen -> useEffect[error], error: " + error,
    });
    crashlytics().recordError(error);
  }
}, [error]);

useLayoutEffect(() => {
  navigation.setOptions({
    headerLeft: () => (
      <HeaderButtons HeaderButtonComponent={MaterialHeaderButton}>
        <Item
          color={
            colorScheme === "dark" ? Colors.dark.text : Colors.light.text
          }
          iconName={
            route.params && route.params.showIcon ? "arrow-back" : undefined
          }
          onPress={() => navigation.goBack()}
          title="back"
        />
      </HeaderButtons>
    ),
  });
}, [navigation]);

const getUserLocationHandler: () => Promise<void> = useCallback(async () => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> getUserLocationHandler",
  });
  setError(new Error(""));
  setIsLoading(true);

  if (Platform.OS !== "web") {
    const { status } =
      await Location.requestForegroundPermissionsAsync();

    analytics().logEvent("custom_log", {
      description: "--- Analytics: screens -> EditEventScreen -> getUserLocationHandler, status: " + status,
    });
    analytics().logEvent("custom_log", {
      description: "--- Analytics: screens -> EditEventScreen -> getUserLocationHandler, Platform.OS: " + Platform.OS,
    });
  }

  try {
    const location: Location.LocationObject = await Location.getCurrentPositionAsync({});

    analytics().logEvent("custom_log", {
      description: "--- Analytics: screens -> EditEventScreen -> getUserLocationHandler -> try, location: " + location,
    });
    setInitialRegionValue({
      latitude: location.coords.latitude,
      longitude: location.coords.longitude,
      latitudeDelta: 10,
      longitudeDelta: 10,
    });
  } catch (error: unknown) {
    if (error instanceof Error) {
      Alert.alert(
        "Error ❌",
        "We couldn't fetch your location.\nPlease give us the permissions, and it's essential to use TamoTam!",
        [{ text: "Okay" }]
      );

      analytics().logEvent("custom_log", {
        description: "--- Analytics: screens -> EditEventScreen -> getUserLocationHandler -> catch, error: " + error,
      });
      crashlytics().recordError(error);
      setError(new Error(error.message));
    }
  } finally {
    analytics().logEvent("custom_log", {
      description: "--- Analytics: screens -> EditEventScreen -> getUserLocationHandler -> finally",
    });
    setIsLoading(false);
  }
}, [dispatch, setError, setIsLoading]);

useEffect(() => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> useEffect[getUserLocationHandler]",
  });
  getUserLocationHandler();
}, [getUserLocationHandler]);

if (isLoading) {
  return (
    <View style={styles.centered}>
      <ActivityIndicator
        color={colorScheme === "dark" ? Colors.dark.text : Colors.light.text}
        size="large"
      />
    </View>
  );
}

if (internetState.isConnected === false) {
  return (
    <View style={styles.centered}>
      <StyledText style={styles.title}>
        Please turn on the Internet to use TamoTam.
      </StyledText>
    </View>
  );
}

if (selectedLocation) {
  markerCoordinates = {
    latitude: selectedLocation.latitude,
    longitude: selectedLocation.longitude,
  };
}

const onDescriptionChange: (text: SetStateAction<string>) => void = (
  text: SetStateAction<string>
) => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onDescriptionChange, text: " + text,
  });
  setDescriptionValue(text);
};

const onDateTimeChange: (
  _event: any,
  selectedValueDate: Date | undefined
) => void = (_event: any, selectedValueDate: Date | undefined) => {
  if (_event.type === "dismissed") {
    setShowDatepicker(false);
    return;
  }

  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onDateTimeChange, _event: " + _event,
  });
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onDateTimeChange, selectedValueDate: " + selectedValueDate,
  });
  setSelectedDate(selectedValueDate);
  setShowDatepicker(false);
};

const onImageChange: (imagePath: string) => void = (imagePath: string) => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onImageChange, imagePath: " + imagePath,
  });
  setSelectedImage(imagePath);
};

const onLocationChange: (e: {
  nativeEvent: {
    coordinate: Coordinate;
  };
}) => void = (e: { nativeEvent: { coordinate: Coordinate } }) => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onLocationChange, e.nativeEvent.coordinate: " + e.nativeEvent.coordinate,
  });
  setSelectedLocation({
    latitude: e.nativeEvent.coordinate.latitude,
    longitude: e.nativeEvent.coordinate.longitude,
  });
};

const onShowDatePicker: () => void = () => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onShowDatePicker, text: ",
  });
  showDateTimeMode("date");
};

const onShowTimePicker: () => void = () => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onShowTimePicker, text: ",
  });
  showDateTimeMode("time");
};

const onTitleChange: (text: SetStateAction<string>) => void = (
  text: SetStateAction<string>
) => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onTitleChange, text: " + text,
  });
  setTitleValue(text);
};

const showDateTimeMode: (currentMode: string) => void = (currentMode: string) => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> showDateTimeMode, currentMode: " + currentMode,
  });
  setShowDatepicker(true);
  setDateTimeMode(currentMode);
};

const onSaveHandler: () => void = () => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> onSaveHandler",
  });
  setError(new Error(""));
  setIsLoading(true);

  try {
    const newEvent: Event = {
      id: eventId,
      date: selectedDate,
      description: descriptionValue
        ? descriptionValue
        : selectedEvent.description,
      imageUrl: selectedImage ? selectedImage : selectedEvent.imageUrl,
      isUserEvent: selectedEvent.isUserEvent,
      latitude: markerCoordinates.latitude,
      longitude: markerCoordinates.longitude,
      title: titleValue ? titleValue : selectedEvent.title,
    };

    analytics().logEvent("custom_log", {
      description: "--- Analytics: screens -> EditEventScreen -> onSaveHandler -> try, newEvent: " + newEvent,
    });
    dispatch(updateEvent(newEvent));
  } catch (error: unknown) {
    if (error instanceof Error) {
      Alert.alert(
        "Error ❌",
        "TamoTam couldn't save the changes.\nTry one more time!",
        [{ text: "Okay" }]
      );

      analytics().logEvent("custom_log", {
        description: "--- Analytics: screens -> EditEventScreen -> onSaveHandler -> catch, error: " + error,
      });
      crashlytics().recordError(error);
      setError(new Error(error.message));
    }
  } finally {
    analytics().logEvent("custom_log", {
      description: "--- Analytics: screens -> EditEventScreen -> onSaveHandler -> finally",
    });
    setIsLoading(false);
  }

  navigation.goBack();
};

const Map: () => JSX.Element = () => (
  <View style={styles.centered}>
    <MapView
      customMapStyle={CustomMapStyles.CUSTOM_MAP_STYLES}
      followsUserLocation={true}
      initialRegion={initialRegionValue}
      onPress={onLocationChange}
      provider={PROVIDER_GOOGLE}
      ref={mapRef}
      showsUserLocation={true}
      style={styles.map}
    >
      {markerCoordinates && (
        <Marker
          coordinate={markerCoordinates}
          icon={selectedEvent.isUserEvent ? require("../assets/images/icon-map-user-event.png") : require("../assets/images/icon-map-tamotam-event.png")}
          tracksViewChanges={false}
          title="Picked Location"
        ></Marker>
      )}
    </MapView>
  </View>
);

return (
  <KeyboardAvoidingView
    behavior="position"
    style={[
      styles.screen,
      {
        backgroundColor:
          colorScheme === "dark"
            ? Colors.dark.background
            : Colors.light.background,
      },
    ]}
  >
    <ScrollView>
      {!selectedEvent.latitude || !selectedEvent.longitude ?
        <View style={styles.centered}>
          <StyledText style={styles.title}>
            Problem with obtaining coordinates.
          </StyledText>
        </View> :
        <Map />
      }
      <View style={styles.form}>
        <StyledText style={styles.label}>Title</StyledText>
        <TextInput
          defaultValue={selectedEvent ? selectedEvent.title : ""}
          onChangeText={onTitleChange}
          style={[
            styles.textInput,
            {
              color:
                colorScheme === "dark" ? Colors.dark.text : Colors.light.text,
            },
          ]}
        />
        <StyledText style={styles.label}>Description</StyledText>
        <TextInput
          defaultValue={selectedEvent ? selectedEvent.description : ""}
          onChangeText={onDescriptionChange}
          style={[
            styles.textInput,
            {
              color:
                colorScheme === "dark" ? Colors.dark.text : Colors.light.text,
            },
          ]}
        />
        <View style={styles.dateTimeButtonsContainer}>
          <View>
            <Button
              color={
                colorScheme === "dark" ? Colors.dark.text : Colors.light.text
              }
              icon="calendar-edit"
              mode="text"
              onPress={onShowDatePicker}
            >
              Pick date
            </Button>
          </View>
          <View>
            <Button
              color={
                colorScheme === "dark" ? Colors.dark.text : Colors.light.text
              }
              icon="clock-outline"
              mode="text"
              onPress={onShowTimePicker}
            >
              Pick time
            </Button>
          </View>
        </View>
        {showDatepicker && (
          <DateTimePicker
            display="spinner"
            is24Hour={true}
            maximumDate={
              new Date(new Date().setFullYear(new Date().getFullYear() + 1))
            }
            minimumDate={new Date()}
            mode={dateTimeMode}
            onChange={onDateTimeChange}
            testID="dateTimePicker"
            textColor={
              colorScheme === "dark" ? Colors.dark.text : Colors.light.text
            }
            value={selectedDate}
          />
        )}
        <View style={styles.centered}>
          <StyledText>Date: {new Date(selectedDate).toLocaleDateString()}</StyledText>
          <StyledText>Time: {new Date(selectedDate).toLocaleTimeString([], {
            hour: "2-digit",
            minute: "2-digit",
          })}</StyledText>
        </View>
        <SelectImage
          existingImageUrl={
            selectedEvent.imageUrl && typeof selectedEvent.imageUrl === "string"
              ? selectedEvent.imageUrl
              : require("../assets/images/no-image.jpeg")
          }
          onImageTaken={onImageChange}
        />
        <Button
          color={
            colorScheme === "dark" ? Colors.dark.text : Colors.light.text
          }
          icon="check-circle-outline"
          onPress={onSaveHandler}
        >
          Save
        </Button>
      </View>
    </ScrollView>
  </KeyboardAvoidingView>
);
}

const styles = StyleSheet.create({
centered: {
  alignItems: "center",
  flex: 1,
  justifyContent: "center",
},
dateTimeButtonsContainer: {
  flexDirection: "row",
  justifyContent: "space-around",
  marginVertical: 20,
},
form: {
  marginHorizontal: 30,
},
label: {
  fontSize: 18,
  marginBottom: 15,
},
map: {
  height: Dimensions.get("window").height / 2,
  width: Dimensions.get("window").width,
},
screen: {
  flex: 1,
},
textInput: {
  borderBottomColor: "#ccc",
  borderBottomWidth: 1,
  marginBottom: 15,
  paddingVertical: 4,
  paddingHorizontal: 2,
},
title: {
  fontSize: 20,
  fontWeight: "bold",
  textAlign: "center",
},
});
How it could have been done?
import analytics from "@react-native-firebase/analytics";
import React, {
useCallback,
useEffect,
useLayoutEffect,
useState,
Dispatch,
} from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNetInfo, NetInfoState } from "@react-native-community/netinfo";
import { Event } from "../interfaces/event";
import { Region } from "../interfaces/region";
import LoadingView from '@/components/common/LoadingView.ts';
import InternetConnectionErrorView from '@/components/errors/InternetConnectionErrorView.ts';
import EditEventScreen from '@/components/edit-screen/EditEventScreen.ts';
import EditEventHeaderButtons from '@/components/edit-screen/EditEventHeaderButtons.ts';
import reportError from '@/services/reportError';
import userLocationHandlerCallback from '@/services/edit-event/userLocationHandlerCallback';

export default function EditEventScreen({ navigation, route }: any) {
const dispatch: Dispatch<any> = useDispatch<Dispatch<any>>();

const eventId: number = route.params.eventId;

const internetState: NetInfoState = useNetInfo();

const selectedEvent: Event = useSelector<any, any>((state: any) =>
  state.events.savedEvents.find((event: Event) => event.id === eventId)
);

const [error, setError] = useState<Error>(new Error());

const [initialRegionValue, setInitialRegionValue] = useState<Region>({
  latitude: selectedEvent.latitude,
  longitude: selectedEvent.longitude,
  latitudeDelta: 10,
  longitudeDelta: 10,
});

const [isLoading, setIsLoading] = useState<boolean>(false);

useEffect(() => { reportError(error, 'screens -> EditEventScreen -> useEffect[error]') }, [error]);

useLayoutEffect(() => {
  navigation.setOptions({
    headerLeft: () => <EditEventHeaderButtons goBack={navigation.goBack} />,
  });
}, [navigation]);

const getUserLocationHandler: () => Promise<void> = useCallback(() => userLocationHandlerCallback(setError, setIsLoading, setInitialRegionValue), [dispatch, setError, setIsLoading]);

useEffect(() => {
  analytics().logEvent("custom_log", {
    description: "--- Analytics: screens -> EditEventScreen -> useEffect[getUserLocationHandler]",
  });
  getUserLocationHandler();
}, [getUserLocationHandler]);

if (isLoading) return <LoadingView />

if (internetState.isConnected === false) return <InternetConnectionErrorView />

return (
  <EditEventScreen initialRegionValue={initialRegionValue} />
);
}

Changes

So instead of putting everything into one file, I would have splitted it into multiple smaller files (EditEventScreenStyles.ts, LoadingView.ts, InternetConnectionErrorView.ts, EditEventScreen.ts, reportError, EditEventHeaderButtons, userLocationHandlerCallback) and renamed it to the EditEventView.ts.

Sometimes images from API aren't displayed

For some reason, some of the images from API's are displayed, whereas some are not.

In addition to that, let's not rely on the Android simulator. Android device, iOS device and iOS simulator show the same images. Whereas the Android simulator is way less.
I've described the problem here: https://stackoverflow.com/a/73217455/11127383.

What comes to my mind is to use a different image from the API response, but the weird thing is sometimes the same images are displayed, and sometimes not. It might take lots of time to debug, so for now, let's move on.

Setting up images for Android was a headache, as per https://stackoverflow.com/a/73217985/11127383. The https://github.com/tamotam-com/tamotam-app/releases/tag/v0.6.0 seems to be relatively stable regarding that aspect.

No Internet View in 1 place

The logic was on every screen, because when trying to put this in App.tsx (c6a3605) or navigation/index.tsx (5619f8a) to avoid code duplication then whenever the internet was back, the user was redirected to the starting screen of the app. We want: when the internet is back, to be back on the screen where the user ended.

The other solution causes several popups (1693c04), but it is as we wanted to return the user to the same screen.

Currently, the final solution is to use Alert in App.tsx, whereas No Internet View on each screen (9e59911).

Hopefully, someone will update here: https://stackoverflow.com/a/73080730/11127383

Add (Date) Filter

Add, initially only date, filter. Later on, maybe also other filters, but for now it's too much in the future.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.