Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

Commit a86b8dd

Browse files
committed
Code clean, Added readme & DarkMode
1 parent fb51242 commit a86b8dd

File tree

27 files changed

+367
-206
lines changed

27 files changed

+367
-206
lines changed

README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# React Native Loop
2+
3+
## Features
4+
5+
<dl>
6+
<dt>UI Scaling</dt>
7+
<dd>Scales UI on any size of device wether it be tablet, small screen mobile or phablet.</dd>
8+
9+
<dt>Offline-first</dt>
10+
<dd>Run app without an internet connection. All assets are saved locally.</dd>
11+
12+
<dt>Simple UI</dt>
13+
<dd>Just simple and straight forward UI.</dd>
14+
15+
<dt>Modularized Structure</dt>
16+
<dd>Helps maintain a readable code, easy to fix and more room to add new features.</dd>
17+
18+
<dt>Open Source</dt>
19+
<dd>Any piece of code is free to use anywhere except UI Designs you've to ask permission from relevant designers. Designer's information can be found in app.</dd>
20+
</dl>
21+
22+
## Requirements to run locally
23+
24+
> Just in case if you ran into errors make sure you're using correct react-native & nodejs version.
25+
26+
- Run `npm run setup` to install required global npm packages with correct versions
27+
28+
## Getting started with code
29+
30+
> Please note that this project's code is not meant for beginners! If you're just getting started with flutter I recommend you to explore some ToDo and basic setState apps and get yourself familiar with react eco-system becuase in this project intermediate and advance implementations are use which will confuse you and won't help much in terms of learning.
31+
32+
- `init.js` initialize default settings like styles, theme & API.
33+
- `UI.dart` provides constant for building responsive UI.
34+
- `blocs/` Intially I was going to implement Rest APIs but that seemed unnecessary & a lot of work So I'll probably remove `blocs/` in futrue.
35+
- `configs/AppDimensions.dart` this is the magical file. It provides the app with:
36+
- My custom size unit based on device's width, height & pixel density.
37+
- Responsive containers.
38+
- Padding multiplier unit (I learned it with experience instead of using 1,2,3px should use multiplier. it helps maintain constancy around the app).
39+
- `Widgets/Screen.dart` This widget is necessary when building a new screen.
40+
- configure theme & font style.
41+
- You can show popUps. `final screenKey = GlobalKey<ScreenState>();` & `this.screenKey.showPopUp(message: "your message");`
42+
- It also recieve a `belowBuilder` parameter which builds custom background (This enables us to build Parallax, Animated background & Any thing you could imagine in background). you can find an example in `Screens/Download/Download.dart`
43+
- Code structure is pretty much simple.
44+
- Don't import anything form ScreenA in ScreenB.
45+
- Don't import anthing from Screen/Widget in universal files.
46+
- Don't import anthing from ScreenA specific Widget in universal files.
47+
- Each Screen will have `Dimensions.dart` where you can write Screen's responsive logic.
48+
- I didn't use snake_case in naming convention just becuase I don't prefer it.
49+
- I use `this` for class's properties & methods I helps keep track of vriables & functions.
50+
51+
52+
## Show support
53+
54+
> **If you like the project and want to appreciate my effort. Then please perform any of these steps :)**
55+
56+
- Star this repository.
57+
- Rate the app on <a href="https://play.google.com/store/apps/details?id=com.onemdev.flutter_ui_challenges" target="_playstore">Play Store</a>.
58+
- Endorse my skills on my <a href="https://www.linkedin.com/in/hackerhgl" target="linkedin">linkedin Profile</a>.
59+
- Favorite my gigs on <a href="https://www.fiverr.com/hackerhgl" target="fiver">Fiverr</a>.
60+
- Give a recommendation on <a href="https://www.freelancer.com/u/hackerhgl" target="freelance">Freelancer</a>.
61+
62+
## Download
63+
64+
<div id="downloads">
65+
<a href="https://play.google.com/store/apps/details?id=com.onemdev.flutter_ui_challenges">
66+
<img src="https://raw.githubusercontent.com/hackerhgl/flutter-ui-designs/master/.github/assets/google-play.png" alt="Play Store badge" width="200" />
67+
</a>
68+
<a href="https://github.com/hackerhgl/flutter-ui-designs/releases/latest/download/app-release.apk">
69+
<img src="https://raw.githubusercontent.com/hackerhgl/flutter-ui-designs/master/.github/assets/android.png" alt="Android badge" width="200" />
70+
</a>
71+
</div>
72+
73+
## License
74+
75+
This project is licensed under the MIT license, Copyright (c) 2020 Hamza Iqbal. For more information see `LICENSE.md`.

android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<activity
2727
android:name=".MainActivity"
2828
android:label="@string/app_name"
29-
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
29+
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
3030
android:screenOrientation="portrait"
3131
android:windowSoftInputMode="adjustResize">
3232
<intent-filter>

app/configs/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const colors = {
99
...defaultColors,
1010
accent: Color('#1565C0'),
1111
primary: Color('#cc2f2c'),
12-
darkBackground: Color('#1b1b1a'),
12+
darkBackground: Color('#1c1c1e'),
1313
textColor: Color('#1b1b1a'),
1414
};
1515

app/contexts/Theme/index.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React, { createContext, useState } from 'react';
2+
import { StatusBar, Text } from 'react-native';
3+
import PropTypes from 'prop-types';
4+
import AsyncStorage from '@react-native-community/async-storage';
5+
import { useDarkMode, DarkModeProvider } from 'react-native-dark-mode';
6+
7+
import { setText } from 'init';
8+
9+
export const ThemeContext = createContext();
10+
11+
const KEYS = {
12+
THEME: 'theme',
13+
};
14+
15+
export const THEMES = {
16+
default: 'default',
17+
light: 'light',
18+
dark: 'dark',
19+
};
20+
21+
export default function ThemeContextProvider({ children }) {
22+
const [state, setState] = useState({
23+
init: false,
24+
theme: THEMES.default,
25+
});
26+
const safeSetState = (newState) => setState({ ...state, ...newState });
27+
const isDarkMode = useDarkMode();
28+
const isDark = state.theme === THEMES.dark || (state.theme === THEMES.default && isDarkMode);
29+
const props = {};
30+
31+
function setDefaultStatusBar() {
32+
StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content', true);
33+
}
34+
35+
setDefaultStatusBar();
36+
37+
async function setTheme(theme) {
38+
if (theme === state.theme) {
39+
return;
40+
}
41+
try {
42+
safeSetState({ theme });
43+
AsyncStorage.setItem(KEYS.THEME, theme);
44+
} catch (e) {
45+
//
46+
}
47+
}
48+
49+
async function initApp() {
50+
try {
51+
const cache = await AsyncStorage.getItem(KEYS.THEME);
52+
if (cache && cache !== state.mp3) {
53+
safeSetState({ init: true, theme: cache });
54+
} else {
55+
safeSetState({ init: true });
56+
}
57+
} catch (e) {
58+
// e
59+
}
60+
}
61+
62+
if (!state.init) {
63+
initApp();
64+
}
65+
66+
if (state.theme !== THEMES.default) {
67+
props.mode = state.theme;
68+
}
69+
70+
setText(isDark);
71+
72+
return (
73+
<DarkModeProvider {...props}>
74+
<ThemeContext.Provider
75+
value={{
76+
...state,
77+
setTheme,
78+
setDefaultStatusBar,
79+
}}
80+
>
81+
{children}
82+
</ThemeContext.Provider>
83+
</DarkModeProvider>
84+
);
85+
}
86+
87+
ThemeContextProvider.propTypes = {
88+
children: PropTypes.any.isRequired,
89+
};

app/engine/index.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,9 @@ export default function useEngine(forceLevel, player) {
6969
url: `data:image/jpeg;base64,${base64}`,
7070
filename: `rn-loop-game-${new Date().getTime()}`,
7171
});
72-
73-
// await sleep(4000);
7472
player.play();
7573
} catch (e) {
76-
console.log(e);
77-
7874
player.play();
79-
// e
8075
}
8176
}
8277

@@ -125,7 +120,6 @@ export default function useEngine(forceLevel, player) {
125120
grid,
126121
level,
127122
theme,
128-
// theme: levels[level].theme.dark,
129123
success,
130124
capture,
131125
setRotate,

app/index.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ import { isIOS } from 'rn-hgl/platform';
1111
import { colors } from 'configs';
1212

1313
import SettingsContextProvider from 'contexts/Settings';
14+
import ThemeContextProvider from 'contexts/Theme';
1415

1516
import Navigator from './Navigator';
16-
import routesMap from './routes.map';
1717

1818
function onRouteChange(prevState, currentState) {
1919
const currentScreen = getActiveRouteName(currentState);
2020
const prevScreen = getActiveRouteName(prevState);
2121
if (prevScreen !== currentScreen) {
2222
analytics().setCurrentScreen(currentScreen, currentScreen);
23-
StatusBar.setBarStyle(routesMap[currentScreen].statusBar);
2423
}
2524
}
2625

@@ -32,21 +31,24 @@ function initStatusbar() {
3231
}
3332

3433
function App() {
34+
const timeout = setTimeout(initStatusbar, 100);
35+
3536
if (UIManager.setLayoutAnimationEnabledExperimental) {
3637
UIManager.setLayoutAnimationEnabledExperimental(true);
3738
}
3839

39-
const timeout = setTimeout(initStatusbar, 100);
40-
4140
initStatusbar();
41+
4242
useEffect(() => () => {
4343
clearTimeout(timeout);
4444
});
4545

4646
return (
47-
<SettingsContextProvider>
48-
<Navigator onNavigationStateChange={onRouteChange} />
49-
</SettingsContextProvider>
47+
<ThemeContextProvider>
48+
<SettingsContextProvider>
49+
<Navigator onNavigationStateChange={onRouteChange} />
50+
</SettingsContextProvider>
51+
</ThemeContextProvider>
5052
);
5153
}
5254

app/index.old.text

Lines changed: 0 additions & 59 deletions
This file was deleted.

app/init.js

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,27 @@ import { getFont } from 'utils/fonts';
1212
import { colors, typography } from 'configs';
1313

1414
const sourceRender = Text.render;
15-
Text.render = function render(props, ref) {
16-
return sourceRender.apply(this, [
17-
{
18-
...props,
19-
style: [
20-
{ ...getFont(), color: colors.dark.string(), fontSize: typography.label1 },
21-
props.style,
22-
],
23-
},
24-
ref,
25-
]);
26-
};
15+
16+
export function setText(isDark = false) {
17+
Text.render = function render(props, ref) {
18+
return sourceRender.apply(this, [
19+
{
20+
...props,
21+
style: [
22+
{
23+
...getFont(),
24+
color: isDark ? colors.white : colors.dark,
25+
fontSize: typography.label1,
26+
},
27+
props.style,
28+
],
29+
},
30+
ref,
31+
]);
32+
};
33+
}
34+
35+
setText();
2736

2837
enableScreens();
2938
analytics().setAnalyticsCollectionEnabled(true);

app/routes.map.js

Lines changed: 0 additions & 10 deletions
This file was deleted.

app/screens/AboutApp/data.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export const links = [
2+
{
3+
label: 'Github',
4+
icon: 'github-circle',
5+
url: 'https://github.com/hackerhgl/react-native-loop-clone',
6+
},
7+
{
8+
icon: 'google-play',
9+
label: 'Original App',
10+
url: 'https://play.google.com/store/apps/details?id=com.balysv.loop',
11+
},
12+
{
13+
icon: 'apple',
14+
label: 'Original App',
15+
url: 'https://apps.apple.com/us/app/infinity-loop-endless-zen/id977028266',
16+
},
17+
{
18+
icon: 'web',
19+
label: 'Orange Free Sounds',
20+
url: 'http://www.orangefreesounds.com/',
21+
},
22+
];

0 commit comments

Comments
 (0)