Compare commits

..

No commits in common. "a4bbd841367ce2fc7609993c103e821ad466ca60" and "1e0cc867b8bb96db2e7a05821fb6e3439ac2627d" have entirely different histories.

25 changed files with 335 additions and 202 deletions

View file

@ -80,8 +80,8 @@ project.ext.react = [
enableHermes: true,
]
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
/**
* Set this to true to create two separate APKs instead of one:
@ -134,7 +134,6 @@ android {
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 16
versionName "2.0.0"
missingDimensionStrategy 'react-native-camera', 'general'
}
splits {
abi {
@ -191,6 +190,7 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.facebook.react:react-native:+" // From node_modules
addUnimodulesDependencies()
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";

View file

@ -9,6 +9,16 @@ import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import fr.amicaleinsat.application.generated.BasePackageList;
import org.unimodules.adapters.react.ReactAdapterPackage;
import org.unimodules.adapters.react.ModuleRegistryAdapter;
import org.unimodules.adapters.react.ReactModuleRegistryProvider;
import org.unimodules.core.interfaces.Package;
import org.unimodules.core.interfaces.SingletonModule;
import expo.modules.constants.ConstantsPackage;
import expo.modules.permissions.PermissionsPackage;
import expo.modules.filesystem.FileSystemPackage;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
@ -16,6 +26,10 @@ import java.util.List;
import javax.annotation.Nullable;
public class MainApplication extends Application implements ReactApplication {
private final ReactModuleRegistryProvider mModuleRegistryProvider = new ReactModuleRegistryProvider(
new BasePackageList().getPackageList(),
null
);
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
@ -26,6 +40,7 @@ public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider));
return packages;
}

View file

@ -0,0 +1,25 @@
package fr.amicaleinsat.application.generated;
import java.util.Arrays;
import java.util.List;
import org.unimodules.core.interfaces.Package;
public class BasePackageList {
public List<Package> getPackageList() {
return Arrays.<Package>asList(
new expo.modules.barcodescanner.BarCodeScannerPackage(),
new expo.modules.camera.CameraPackage(),
new expo.modules.constants.ConstantsPackage(),
new expo.modules.errorrecovery.ErrorRecoveryPackage(),
new expo.modules.filesystem.FileSystemPackage(),
new expo.modules.font.FontLoaderPackage(),
new expo.modules.imageloader.ImageLoaderPackage(),
new expo.modules.keepawake.KeepAwakePackage(),
new expo.modules.lineargradient.LinearGradientPackage(),
new expo.modules.location.LocationPackage(),
new expo.modules.permissions.PermissionsPackage(),
new expo.modules.sqlite.SQLitePackage(),
new expo.modules.webbrowser.WebBrowserPackage()
);
}
}

View file

@ -1,5 +1,8 @@
rootProject.name = 'Campus'
apply from: '../node_modules/react-native-unimodules/gradle.groovy'
includeUnimodulesProjects()
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
applyNativeModulesSettingsGradle(settings)

View file

@ -1,3 +1,11 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo', '@babel/preset-flow'],
env: {
production: {
plugins: ['react-native-paper/babel'],
},
},
};
};

View file

@ -1,4 +1,8 @@
import {AppRegistry} from 'react-native';
import {registerRootComponent} from 'expo';
import App from './App';
AppRegistry.registerComponent('main', () => App);
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

View file

@ -71,9 +71,5 @@
<string>Automatic</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIAppFonts</key>
<array>
<string>MaterialCommunityIcons.ttf</string>
</array>
</dict>
</plist>

View file

@ -50,7 +50,4 @@ target 'Campus' do
pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications.podspec"
pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec"
# Vector Icons
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
end

View file

@ -1,17 +0,0 @@
/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* @format
*/
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};

View file

@ -1,13 +1,12 @@
{
"name": "campus",
"version": "2.0.0",
"private": true,
"scripts": {
"start": "react-native start",
"android": "react-native run-android",
"ios": "react-native run-ios",
"web": "expo start --web",
"test": "jest",
"lint": "eslint ."
"testw": "jest --watch",
"testc": "jest --coverage"
},
"jest": {
"preset": "react-native",
@ -19,12 +18,15 @@
]
},
"dependencies": {
"@nartc/react-native-barcode-mask": "^1.1.9",
"@expo/vector-icons": "^10.0.0",
"@react-native-community/masked-view": "0.1.6",
"@react-navigation/bottom-tabs": "^5.1.1",
"@react-navigation/drawer": "^5.1.1",
"@react-navigation/native": "^5.0.9",
"@react-navigation/stack": "^5.1.1",
"expo": "^37.0.0",
"expo-barcode-scanner": "~8.1.0",
"expo-camera": "latest",
"i18n-js": "^3.3.0",
"react": "~16.9.0",
"react-dom": "16.9.0",
@ -34,7 +36,6 @@
"react-native-appearance": "~0.3.3",
"react-native-autolink": "^3.0.0",
"react-native-calendars": "^1.260.0",
"react-native-camera": "^3.23.1",
"react-native-collapsible": "^1.5.2",
"react-native-gesture-handler": "~1.6.0",
"react-native-image-modal": "^1.0.6",
@ -49,21 +50,22 @@
"react-native-safe-area-context": "0.7.3",
"react-native-screens": "~2.2.0",
"react-native-splash-screen": "^3.2.0",
"react-native-vector-icons": "^6.6.0",
"react-native-unimodules": "~0.9.0",
"react-native-web": "~0.11.7",
"react-native-webview": "8.1.1",
"react-navigation-collapsible": "^5.5.0",
"react-navigation-header-buttons": "^3.0.5"
},
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/runtime": "^7.6.2",
"@react-native-community/eslint-config": "^0.0.5",
"babel-jest": "^24.9.0",
"eslint": "^6.5.1",
"jest": "^24.9.0",
"metro-react-native-babel-preset": "^0.58.0",
"react-test-renderer": "16.9.0",
"flow-bin": "^0.122.0"
}
"@babel/core": "^7.9.0",
"babel-jest": "~25.2.6",
"jest": "^25.1.0",
"react-test-renderer": "^16.13.1",
"@babel/cli": "^7.8.4",
"@babel/preset-flow": "^7.9.0",
"babel-preset-expo": "^8.1.0",
"flow-bin": "^0.122.0",
"jest-extended": "^0.11.5"
},
"private": true
}

View file

@ -12,7 +12,8 @@ type Props = {
title: string,
subtitle?: string,
left?: (props: { [keys: string]: any }) => React.Node,
opened: boolean,
startOpen: boolean,
keepOpen: boolean,
unmountWhenCollapsed: boolean,
children?: React.Node,
}
@ -23,51 +24,36 @@ type State = {
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
class AnimatedAccordion extends React.Component<Props, State> {
class AnimatedAccordion extends React.PureComponent<Props, State> {
static defaultProps = {
opened: false,
startOpen: false,
keepOpen: false,
unmountWhenCollapsed: false,
}
chevronRef: { current: null | AnimatedListIcon };
chevronIcon: string;
animStart: string;
animEnd: string;
state = {
expanded: this.props.opened,
expanded: false,
}
constructor(props) {
super(props);
this.chevronRef = React.createRef();
this.setupChevron();
}
setupChevron() {
if (this.state.expanded) {
this.chevronIcon = "chevron-up";
this.animStart = "180deg";
this.animEnd = "0deg";
} else {
this.chevronIcon = "chevron-down";
this.animStart = "0deg";
this.animEnd = "180deg";
}
componentDidMount() {
if (this.props.startOpen)
this.toggleAccordion();
}
toggleAccordion = () => {
if (this.chevronRef.current != null)
this.chevronRef.current.transitionTo({rotate: this.state.expanded ? this.animStart : this.animEnd});
this.setState({expanded: !this.state.expanded})
if (!this.props.keepOpen) {
if (this.chevronRef.current != null)
this.chevronRef.current.transitionTo({rotate: this.state.expanded ? '0deg' : '180deg'});
this.setState({expanded: !this.state.expanded})
}
};
shouldComponentUpdate(nextProps: Props) {
this.state.expanded = nextProps.opened;
this.setupChevron();
return true;
}
render() {
const colors = this.props.theme.colors;
return (
@ -81,13 +67,13 @@ class AnimatedAccordion extends React.Component<Props, State> {
right={(props) => <AnimatedListIcon
ref={this.chevronRef}
{...props}
icon={this.chevronIcon}
icon={"chevron-down"}
color={this.state.expanded ? colors.primary : undefined}
useNativeDriver
/>}
left={this.props.left}
/>
<Collapsible collapsed={!this.state.expanded}>
<Collapsible collapsed={!this.props.keepOpen && !this.state.expanded}>
{!this.props.unmountWhenCollapsed || (this.props.unmountWhenCollapsed && this.state.expanded)
? this.props.children
: null}

View file

@ -1,43 +1,22 @@
// @flow
import * as React from 'react';
import {Card, Chip, List, Text} from 'react-native-paper';
import {Card, List, Text} from 'react-native-paper';
import {StyleSheet, View} from "react-native";
import i18n from 'i18n-js';
import AnimatedAccordion from "../../Animations/AnimatedAccordion";
import {isItemInCategoryFilter} from "../../../utils/Search";
import type {category} from "../../../screens/Amicale/Clubs/ClubListScreen";
type Props = {
categories: Array<category>,
onChipSelect: (id: number) => void,
selectedCategories: Array<number>,
categoryRender: Function,
categories: Array<Object>,
}
class ClubListHeader extends React.Component<Props> {
shouldComponentUpdate(nextProps: Props) {
return nextProps.selectedCategories.length !== this.props.selectedCategories.length;
}
getChipRender = (category: category, key: string) => {
const onPress = () => this.props.onChipSelect(category.id);
return <Chip
selected={isItemInCategoryFilter(this.props.selectedCategories, [category.id])}
mode={'outlined'}
onPress={onPress}
style={{marginRight: 5, marginBottom: 5}}
key={key}
>
{category.name}
</Chip>;
};
getCategoriesRender() {
let final = [];
for (let i = 0; i < this.props.categories.length; i++) {
final.push(this.getChipRender(this.props.categories[i], this.props.categories[i].id.toString()));
final.push(this.props.categoryRender(this.props.categories[i], this.props.categories[i].id));
}
return final;
}
@ -48,7 +27,7 @@ class ClubListHeader extends React.Component<Props> {
<AnimatedAccordion
title={i18n.t("clubs.categories")}
left={props => <List.Icon {...props} icon="star"/>}
opened={true}
startOpen={true}
>
<Text style={styles.text}>{i18n.t("clubs.categoriesFilterMessage")}</Text>
<View style={styles.chipContainer}>

View file

@ -19,16 +19,27 @@ type Props = {
theme: CustomTheme,
}
type State = {
expanded: boolean,
}
const LIST_ITEM_HEIGHT = 64;
class GroupListAccordion extends React.Component<Props> {
class GroupListAccordion extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
expanded: props.item.id === "0",
}
}
shouldComponentUpdate(nextProps: Props) {
shouldComponentUpdate(nextProps: Props, nextSate: State) {
if (nextProps.currentSearchString !== this.props.currentSearchString)
this.state.expanded = nextProps.currentSearchString.length > 0;
return (nextProps.currentSearchString !== this.props.currentSearchString)
|| (nextSate.expanded !== this.state.expanded)
|| (nextProps.favoriteNumber !== this.props.favoriteNumber)
|| (nextProps.item.content.length !== this.props.item.content.length);
}
@ -50,6 +61,8 @@ class GroupListAccordion extends React.Component<Props> {
return null;
}
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
render() {
const item = this.props.item;
return (
@ -69,7 +82,6 @@ class GroupListAccordion extends React.Component<Props> {
/>
: null}
unmountWhenCollapsed={true}// Only render list if expanded for increased performance
opened={this.props.item.id === 0 || this.props.currentSearchString.length > 0}
>
{/*$FlowFixMe*/}
<FlatList
@ -79,8 +91,8 @@ class GroupListAccordion extends React.Component<Props> {
keyExtractor={this.keyExtractor}
listKey={item.id.toString()}
// Performance props, see https://reactnative.dev/docs/optimizing-flatlist-configuration
// getItemLayout={this.itemLayout} // Broken with search
// removeClippedSubviews={true}
getItemLayout={this.itemLayout} // Broken with search
removeClippedSubviews={true}
/>
</AnimatedAccordion>
</View>

View file

@ -1,7 +1,7 @@
// @flow
import * as React from 'react';
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {HeaderButton, HeaderButtons} from 'react-navigation-header-buttons';
import {withTheme} from "react-native-paper";
import * as Touchable from "react-native/Libraries/Components/Touchable/TouchableNativeFeedback.android";

View file

@ -2,7 +2,7 @@
import * as React from 'react';
import {Image, Platform, StatusBar, StyleSheet, View} from "react-native";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import {Text} from "react-native-paper";
import i18n from 'i18n-js';
import AppIntroSlider from "react-native-app-intro-slider";

View file

@ -3,7 +3,7 @@
import * as React from 'react';
import {Button, Subheading, withTheme} from 'react-native-paper';
import {StyleSheet, View} from "react-native";
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import i18n from 'i18n-js';
import {ERROR_TYPE} from "../../utils/WebData";
import * as Animatable from 'react-native-animatable';

View file

@ -3,7 +3,7 @@
import * as React from 'react';
import {View} from "react-native";
import {TouchableRipple, withTheme} from 'react-native-paper';
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import * as Animatable from "react-native-animatable";
type Props = {

View file

@ -23,15 +23,13 @@ import ServicesSectionScreen from "../screens/Services/ServicesSectionScreen";
import AmicaleContactScreen from "../screens/Amicale/AmicaleContactScreen";
import {createScreenCollapsibleStack, getWebsiteStack} from "../utils/CollapsibleUtils";
const modalTransition = Platform.OS === 'ios' ? TransitionPresets.ModalPresentationIOS : TransitionPresets.ModalSlideFromBottomIOS;
const defaultScreenOptions = {
gestureEnabled: true,
cardOverlayEnabled: true,
...modalTransition,
...TransitionPresets.ScaleFromCenterAndroid,
};
const modalTransition = Platform.OS === 'ios' ? TransitionPresets.ModalPresentationIOS : TransitionPresets.ModalSlideFromBottomIOS;
const ServicesStack = createStackNavigator();
@ -44,7 +42,7 @@ function ServicesStackComponent() {
>
{createScreenCollapsibleStack("index", ServicesStack, WebsitesHomeScreen, i18n.t('screens.services'))}
{createScreenCollapsibleStack("services-section", ServicesStack, ServicesSectionScreen, "SECTION")}
{createScreenCollapsibleStack("amicale-contact", ServicesStack, AmicaleContactScreen, i18n.t('screens.amicaleAbout'))}
{createScreenCollapsibleStack("amicale-contact", ServicesStack, AmicaleContactScreen, i18n.t('screens.amicaleAbout'), true, {...modalTransition})}
</ServicesStack.Navigator>
);
}
@ -62,7 +60,10 @@ function ProxiwashStackComponent() {
<ProxiwashStack.Screen
name="proxiwash-about"
component={ProxiwashAboutScreen}
options={{title: i18n.t('screens.proxiwash'),}}
options={{
title: i18n.t('screens.proxiwash'),
...modalTransition,
}}
/>
</ProxiwashStack.Navigator>
);
@ -78,14 +79,19 @@ function PlanningStackComponent() {
screenOptions={defaultScreenOptions}
>
<PlanningStack.Screen
name="index"
name="planning"
component={PlanningScreen}
options={{title: i18n.t('screens.planning'),}}
options={{
title: i18n.t('screens.planning'),
}}
/>
<PlanningStack.Screen
name="planning-information"
component={PlanningDisplayScreen}
options={{title: i18n.t('screens.planningDisplayScreen'),}}
options={{
title: i18n.t('screens.planningDisplayScreen'),
...modalTransition,
}}
/>
</PlanningStack.Navigator>
);
@ -124,22 +130,34 @@ function HomeStackComponent(initialRoute: string | null, defaultData: { [key: st
<HomeStack.Screen
name="scanner"
component={ScannerScreen}
options={{title: i18n.t('screens.scanner'),}}
options={{
title: i18n.t('screens.scanner'),
...modalTransition,
}}
/>
<HomeStack.Screen
name="club-information"
component={ClubDisplayScreen}
options={{title: i18n.t('screens.clubDisplayScreen'),}}
options={{
title: i18n.t('screens.clubDisplayScreen'),
...modalTransition,
}}
/>
<HomeStack.Screen
name="feed-information"
component={FeedItemScreen}
options={{title: i18n.t('screens.feedDisplayScreen'),}}
options={{
title: i18n.t('screens.feedDisplayScreen'),
...modalTransition,
}}
/>
<HomeStack.Screen
name="planning-information"
component={PlanningDisplayScreen}
options={{title: i18n.t('screens.planningDisplayScreen'),}}
options={{
title: i18n.t('screens.planningDisplayScreen'),
...modalTransition,
}}
/>
</HomeStack.Navigator>
);
@ -155,7 +173,13 @@ function PlanexStackComponent() {
screenOptions={defaultScreenOptions}
>
{getWebsiteStack("index", PlanexStack, PlanexScreen, "Planex")}
{createScreenCollapsibleStack("group-select", PlanexStack, GroupSelectionScreen, "GROUP SELECT")}
{createScreenCollapsibleStack(
"group-select",
PlanexStack,
GroupSelectionScreen,
"GROUP SELECT",
true,
{...modalTransition})}
</PlanexStack.Navigator>
);
}

View file

@ -2,7 +2,7 @@
import * as React from 'react';
import {Animated, Platform} from "react-native";
import {Searchbar} from 'react-native-paper';
import {Chip, Searchbar} from 'react-native-paper';
import AuthenticatedScreen from "../../../components/Amicale/AuthenticatedScreen";
import i18n from "i18n-js";
import ClubListItem from "../../../components/Lists/Clubs/ClubListItem";
@ -36,7 +36,7 @@ type Props = {
}
type State = {
currentlySelectedCategories: Array<number>,
currentlySelectedCategories: Array<string>,
currentSearchString: string,
}
@ -99,11 +99,13 @@ class ClubListScreen extends React.Component<Props, State> {
this.updateFilteredData(str, null);
};
keyExtractor = (item: club) => item.id.toString();
keyExtractor = (item: club) => {
return item.id.toString();
};
itemLayout = (data, index) => ({length: LIST_ITEM_HEIGHT, offset: LIST_ITEM_HEIGHT * index, index});
getScreen = (data: Array<{ categories: Array<category>, clubs: Array<club> } | null>) => {
getScreen = (data: Array<{categories: Array<category>, clubs: Array<club>} | null>) => {
let categoryList = [];
let clubList = [];
if (data[0] != null) {
@ -129,9 +131,11 @@ class ClubListScreen extends React.Component<Props, State> {
)
};
onChipSelect = (id: number) => this.updateFilteredData(null, id);
onChipSelect(id: string) {
this.updateFilteredData(null, id);
}
updateFilteredData(filterStr: string | null, categoryId: number | null) {
updateFilteredData(filterStr: string | null, categoryId: string | null) {
let newCategoriesState = [...this.state.currentlySelectedCategories];
let newStrState = this.state.currentSearchString;
if (filterStr !== null)
@ -150,11 +154,23 @@ class ClubListScreen extends React.Component<Props, State> {
})
}
getChipRender = (category: category, key: string) => {
const onPress = this.onChipSelect.bind(this, category.id);
return <Chip
selected={isItemInCategoryFilter(this.state.currentlySelectedCategories, [category.id])}
mode={'outlined'}
onPress={onPress}
style={{marginRight: 5, marginBottom: 5}}
key={key}
>
{category.name}
</Chip>;
};
getListHeader() {
return <ClubListHeader
categories={this.categories}
selectedCategories={this.state.currentlySelectedCategories}
onChipSelect={this.onChipSelect}
categoryRender={this.getChipRender}
/>;
}
@ -173,7 +189,7 @@ class ClubListScreen extends React.Component<Props, State> {
return shouldRender;
}
getRenderItem = ({item}: { item: club }) => {
getRenderItem = ({item}: {item: club}) => {
const onPress = this.onListItemPress.bind(this, item);
if (this.shouldRenderItem(item)) {
return (

View file

@ -1,16 +1,15 @@
// @flow
import * as React from 'react';
import {Linking, Platform, StyleSheet, View} from "react-native";
import {Linking, StyleSheet, View} from "react-native";
import {Button, Text, withTheme} from 'react-native-paper';
import {RNCamera} from 'react-native-camera';
import {BarcodeMask} from '@nartc/react-native-barcode-mask';
import {BarCodeScanner} from "expo-barcode-scanner";
import {Camera} from 'expo-camera';
import URLHandler from "../../utils/URLHandler";
import AlertDialog from "../../components/Dialogs/AlertDialog";
import i18n from 'i18n-js';
import CustomTabBar from "../../components/Tabbar/CustomTabBar";
import LoadingConfirmDialog from "../../components/Dialogs/LoadingConfirmDialog";
import {PERMISSIONS, request, RESULTS} from 'react-native-permissions';
type Props = {};
type State = {
@ -41,14 +40,9 @@ class ScannerScreen extends React.Component<Props, State> {
this.requestPermissions();
}
requestPermissions = () => {
if (Platform.OS === 'android')
request(PERMISSIONS.ANDROID.CAMERA).then(this.updatePermissionStatus)
else
request(PERMISSIONS.IOS.CAMERA).then(this.updatePermissionStatus)
};
requestPermissions = () => Camera.requestPermissionsAsync().then(this.updatePermissionStatus);
updatePermissionStatus = (result) => this.setState({hasPermission: result === RESULTS.GRANTED});
updatePermissionStatus = ({status}) => this.setState({hasPermission: status === "granted"});
handleCodeScanned = ({type, data}) => {
if (!URLHandler.isUrlValid(data))
@ -77,6 +71,36 @@ class ScannerScreen extends React.Component<Props, State> {
</View>
}
getOverlay() {
return (
<View style={{flex: 1}}>
<View style={{flex: 1}}>
<View style={{...overlayBackground, top: 0, height: '10%', width: '80%', left: '10%'}}/>
<View style={{...overlayBackground, left: 0, width: '10%', height: '100%'}}/>
<View style={{...overlayBackground, right: 0, width: '10%', height: '100%'}}/>
<View style={{...overlayBackground, bottom: 0, height: '10%', width: '80%', left: '10%'}}/>
</View>
<View style={styles.overlayTopLeft}>
<View style={{...overlayHorizontalLineStyle, top: 0}}/>
<View style={{...overlayVerticalLineStyle, left: 0}}/>
</View>
<View style={styles.overlayTopRight}>
<View style={{...overlayHorizontalLineStyle, top: 0}}/>
<View style={{...overlayVerticalLineStyle, right: 0}}/>
</View>
<View style={styles.overlayBottomLeft}>
<View style={{...overlayHorizontalLineStyle, bottom: 0}}/>
<View style={{...overlayVerticalLineStyle, left: 0}}/>
</View>
<View style={styles.overlayBottomRight}>
<View style={{...overlayHorizontalLineStyle, bottom: 0}}/>
<View style={{...overlayVerticalLineStyle, right: 0}}/>
</View>
</View>
);
}
showHelpDialog = () => {
this.setState({
dialogVisible: true,
@ -109,22 +133,19 @@ class ScannerScreen extends React.Component<Props, State> {
getScanner() {
return (
<RNCamera
onBarCodeRead={this.state.scanned ? undefined : this.handleCodeScanned}
type={RNCamera.Constants.Type.back}
barCodeScannerSettings={{
barCodeTypes: [RNCamera.Constants.BarCodeType.qr],
}}
style={StyleSheet.absoluteFill}
captureAudio={false}
>
<BarcodeMask
backgroundColor={"#000"}
maskOpacity={0.5}
animatedLineThickness={1}
animationDuration={1000}
/>
</RNCamera>
<View style={styles.cameraContainer}>
<Camera
onBarCodeScanned={this.state.scanned ? undefined : this.handleCodeScanned}
type={Camera.Constants.Type.back}
barCodeScannerSettings={{
barCodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
}}
style={StyleSheet.absoluteFill}
ratio={'1:1'}
>
{this.getOverlay()}
</Camera>
</View>
);
}
@ -132,20 +153,22 @@ class ScannerScreen extends React.Component<Props, State> {
return (
<View style={{
...styles.container,
marginBottom: CustomTabBar.TAB_BAR_HEIGHT
marginBottom: CustomTabBar.TAB_BAR_HEIGHT + 20
}}>
{this.state.hasPermission
? this.getScanner()
: this.getPermissionScreen()
}
<Button
icon="information"
mode="contained"
onPress={this.showHelpDialog}
style={styles.button}
>
{i18n.t("scannerScreen.helpButton")}
</Button>
{this.state.hasPermission
? this.getScanner()
: this.getPermissionScreen()
}
<View style={{height: 50}}>
<Button
icon="information"
mode="contained"
onPress={this.showHelpDialog}
style={styles.button}
>
{i18n.t("scannerScreen.helpButton")}
</Button>
</View>
<AlertDialog
visible={this.state.dialogVisible}
onDismiss={this.onDialogDismiss}
@ -162,17 +185,74 @@ class ScannerScreen extends React.Component<Props, State> {
}
}
const borderOffset = '10%';
const overlayBoxStyle = {
position: 'absolute',
width: 25,
height: 25,
};
const overlayLineStyle = {
position: 'absolute',
backgroundColor: "#fff",
borderRadius: 2,
};
const overlayHorizontalLineStyle = {
...overlayLineStyle,
width: '100%',
height: 5,
};
const overlayVerticalLineStyle = {
...overlayLineStyle,
height: '100%',
width: 5,
};
const overlayBackground = {
backgroundColor: "rgba(0,0,0,0.47)",
position: "absolute",
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
cameraContainer: {
marginTop: 'auto',
marginBottom: 'auto',
aspectRatio: 1,
width: '100%',
},
button: {
position: 'absolute',
bottom: 20,
bottom: 5,
width: '80%',
left: '10%'
},
overlayTopLeft: {
...overlayBoxStyle,
top: borderOffset,
left: borderOffset,
},
overlayTopRight: {
...overlayBoxStyle,
top: borderOffset,
right: borderOffset,
},
overlayBottomLeft: {
...overlayBoxStyle,
bottom: borderOffset,
left: borderOffset,
},
overlayBottomRight: {
...overlayBoxStyle,
bottom: borderOffset,
right: borderOffset,
},
});
export default withTheme(ScannerScreen);

View file

@ -36,6 +36,7 @@ class FeedbackScreen extends React.Component<Props> {
}}>
<Button
icon="email"
mode="contained"
style={{
marginLeft: 'auto',
marginTop: 5,
@ -45,6 +46,7 @@ class FeedbackScreen extends React.Component<Props> {
</Button>
<Button
icon="git"
mode="contained"
style={{
marginLeft: 'auto',
marginTop: 5,
@ -54,6 +56,7 @@ class FeedbackScreen extends React.Component<Props> {
</Button>
<Button
icon="facebook"
mode="contained"
style={{
marginLeft: 'auto',
marginTop: 5,

View file

@ -1,12 +1,12 @@
// @flow
import * as React from 'react';
import {Alert, View} from 'react-native';
import {Alert, Platform, View} from 'react-native';
import i18n from "i18n-js";
import WebSectionList from "../../components/Screens/WebSectionList";
import * as Notifications from "../../utils/Notifications";
import AsyncStorageManager from "../../managers/AsyncStorageManager";
// import * as Expo from "expo";
import * as Expo from "expo";
import {Avatar, Banner, Button, Card, Text, withTheme} from 'react-native-paper';
import ProxiwashListItem from "../../components/Lists/Proxiwash/ProxiwashListItem";
import ProxiwashConstants from "../../constants/ProxiwashConstants";
@ -92,18 +92,18 @@ class ProxiwashScreen extends React.Component<Props, State> {
this.setState({machinesWatched: fetchedList})
});
// Get updated watchlist after received notification
// Expo.Notifications.addListener(() => {
// Notifications.getMachineNotificationWatchlist((fetchedList) => {
// this.setState({machinesWatched: fetchedList})
// });
// });
// if (Platform.OS === 'android') {
// Expo.Notifications.createChannelAndroidAsync('reminders', {
// name: 'Reminders',
// priority: 'max',
// vibrate: [0, 250, 250, 250],
// });
// }
Expo.Notifications.addListener(() => {
Notifications.getMachineNotificationWatchlist((fetchedList) => {
this.setState({machinesWatched: fetchedList})
});
});
if (Platform.OS === 'android') {
Expo.Notifications.createChannelAndroidAsync('reminders', {
name: 'Reminders',
priority: 'max',
vibrate: [0, 250, 250, 250],
});
}
}
}

View file

@ -61,7 +61,7 @@ class ProximoListScreen extends React.Component<Props, State> {
constructor(props) {
super(props);
this.listData = this.props.route.params['data']['data'].sort(sortName);
this.listData = this.props.route.params['data']['data'];
this.shouldFocusSearchBar = this.props.route.params['shouldFocusSearchBar'];
this.state = {
currentSearchString: '',

View file

@ -3,7 +3,7 @@
import * as React from 'react';
import {Alert, View} from 'react-native';
import {IconButton, Text, withTheme} from 'react-native-paper';
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
import {MaterialCommunityIcons} from "@expo/vector-icons";
import GameLogic from "./GameLogic";
import Grid from "./components/Grid";
import Preview from "./components/Preview";

View file

@ -1,7 +1,7 @@
// @flow
import {checkNotifications, requestNotifications, RESULTS} from 'react-native-permissions';
// import {Notifications} from 'expo';
import {Notifications} from 'expo';
import AsyncStorageManager from "../managers/AsyncStorageManager";
import LocaleManager from "../managers/LocaleManager";
import passwords from "../../passwords";
@ -40,15 +40,15 @@ export async function askPermissions() {
* @return {Promise<void>}
*/
export async function initExpoToken() {
// let token = AsyncStorageManager.getInstance().preferences.expoToken.current;
// if (token === '') {
// askPermissions().then(() => {
// Notifications.getExpoPushTokenAsync().then((token) => {
// // Save token for instant use later on
// AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.expoToken.key, token);
// });
// });
// }
let token = AsyncStorageManager.getInstance().preferences.expoToken.current;
if (token === '') {
askPermissions().then(() => {
Notifications.getExpoPushTokenAsync().then((token) => {
// Save token for instant use later on
AsyncStorageManager.getInstance().savePref(AsyncStorageManager.getInstance().preferences.expoToken.key, token);
});
});
}
}
/**