├── .watchmanconfig ├── index.js ├── public ├── robots.txt ├── icon.png ├── favicon.ico ├── icon-192.png ├── icon-2048.png ├── icon-512.png ├── manifest.json ├── electron.js ├── index.css └── index.html ├── src ├── op-game │ ├── index.ts │ └── Game.tsx ├── op-home │ ├── index.tsx │ ├── logo-border-dark.png │ ├── logo-border-light.png │ ├── Menu.tsx │ ├── About.tsx │ ├── Logo.tsx │ └── Home.tsx ├── op-intro │ ├── index.ts │ └── Intro.tsx ├── op-splash │ ├── index.ts │ ├── Splash.tsx │ └── Splash.native.tsx ├── op-stats │ ├── index.ts │ └── Stats.tsx ├── op-message │ ├── index.ts │ └── Message.tsx ├── op-success │ ├── index.ts │ └── Success.tsx ├── op-tutorial │ ├── index.tsx │ ├── Description.tsx │ └── Tutorial.tsx ├── react-app-env.d.ts ├── op-core │ ├── index.ts │ ├── Layout.tsx │ ├── Main.tsx │ ├── Router.tsx │ ├── App.tsx │ └── store.ts ├── op-native │ ├── react-native-immersive.ts │ ├── react-native-keep-awake.ts │ ├── react-native-bootsplash.ts │ ├── react-native-sound.native.ts │ ├── react-native-bootsplash.native.ts │ ├── react-native-keep-awake.native.ts │ ├── react-native-immersive.native.ts │ └── react-native-sound.ts ├── op-board │ ├── index.ts │ ├── PointerAwareView.tsx │ ├── Board.tsx │ └── Tile.tsx ├── op-config │ ├── index.ts │ ├── strings.ts │ └── constants.ts ├── op-utils │ ├── delay.ts │ ├── hapticFeedback.ts │ ├── useOnMount.ts │ ├── index.ts │ ├── useAnimation.ts │ ├── useHardwareBackButton.ts │ ├── storage.ts │ ├── pickPuzzle.ts │ ├── sound.ts │ ├── scale.ts │ └── pickPuzzle.test.ts ├── op-design │ ├── index.tsx │ ├── metrics.ts │ ├── colors.ts │ ├── fonts.ts │ └── animations.ts ├── index.native.ts ├── op-common │ ├── index.tsx │ ├── AnimatedLetter.tsx │ ├── Text.tsx │ ├── BottomNav.tsx │ ├── Score.tsx │ ├── Header.tsx │ └── Button.tsx ├── index.web.ts ├── service-worker.ts └── serviceWorkerRegistration.ts ├── .gitattributes ├── .github ├── logo.png ├── web-app-badge.png ├── app-store-badge.png ├── play-store-badge.png ├── iphone-screenshot-dark.png └── iphone-screenshot-light.png ├── .env ├── assets ├── images │ └── icon.png ├── audio │ └── buttonpress.wav └── fonts │ └── Inter-SemiBold.otf ├── .eslintrc ├── android ├── app │ ├── debug.keystore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── strings.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-ldpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ ├── ic_launcher_round.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable │ │ │ │ │ ├── bootsplash.xml │ │ │ │ │ └── ic_launcher_background.xml │ │ │ │ └── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── ic_launcher-web.png │ │ │ ├── assets │ │ │ │ └── fonts │ │ │ │ │ └── Inter-SemiBold.otf │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── mmazzarolo │ │ │ │ │ └── ordinarypuzzles │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ └── AndroidManifest.xml │ │ └── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── mmazzarolo │ │ │ └── ordinarypuzzles │ │ │ └── ReactNativeFlipper.java │ ├── proguard-rules.pro │ ├── build_defs.bzl │ ├── BUCK │ └── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── settings.gradle ├── build.gradle ├── gradle.properties ├── gradlew.bat └── gradlew ├── ios ├── OrdinaryPuzzles │ ├── Images.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── ipad_app76x76.png │ │ │ ├── ipad_app76x76@2x.png │ │ │ ├── ipad_settings29x29.png │ │ │ ├── ipad_spotlight40x40.png │ │ │ ├── iphone_app60x60@2x.png │ │ │ ├── iphone_app60x60@3x.png │ │ │ ├── ipad_settings29x29@2x.png │ │ │ ├── ios_marketing1024x1024.png │ │ │ ├── ipad_notification20x20.png │ │ │ ├── ipad_pro_app83.5x83.5@2x.png │ │ │ ├── ipad_spotlight40x40@2x.png │ │ │ ├── iphone_settings29x29@2x.png │ │ │ ├── iphone_settings29x29@3x.png │ │ │ ├── iphone_spotlight40x40@2x.png │ │ │ ├── iphone_spotlight40x40@3x.png │ │ │ ├── ipad_notification20x20@2x.png │ │ │ ├── iphone_notification20x20@2x.png │ │ │ ├── iphone_notification20x20@3x.png │ │ │ └── Contents.json │ ├── AppDelegate.h │ ├── main.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Info.plist │ └── AppDelegate.m ├── File.swift ├── OrdinaryPuzzles-Bridging-Header.h ├── OrdinaryPuzzles-tvOS-Bridging-Header.h ├── OrdinaryPuzzles.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── OrdinaryPuzzlesTests │ ├── Info.plist │ └── OrdinaryPuzzlesTests.m ├── OrdinaryPuzzles-tvOSTests │ └── Info.plist ├── Podfile ├── OrdinaryPuzzles-tvOS │ └── Info.plist └── OrdinaryPuzzles.xcodeproj │ └── xcshareddata │ └── xcschemes │ ├── OrdinaryPuzzles.xcscheme │ └── OrdinaryPuzzles-tvOS.xcscheme ├── jest.config.js ├── .buckconfig ├── react-native.config.js ├── metro.config.js ├── .vscode └── settings.json ├── babel.config.js ├── config-overrides.js ├── tsconfig.json ├── LICENSE.md ├── .gitignore ├── CONTRIBUTING.md ├── README.md └── package.json /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import "./src/index"; 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /src/op-game/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Game"; 2 | -------------------------------------------------------------------------------- /src/op-home/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./Home"; 2 | -------------------------------------------------------------------------------- /src/op-intro/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Intro"; 2 | -------------------------------------------------------------------------------- /src/op-splash/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Splash"; 2 | -------------------------------------------------------------------------------- /src/op-stats/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Stats"; 2 | -------------------------------------------------------------------------------- /src/op-message/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Message"; 2 | -------------------------------------------------------------------------------- /src/op-success/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Success"; 2 | -------------------------------------------------------------------------------- /src/op-tutorial/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./Tutorial"; 2 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # specific for windows script files 2 | *.bat text eol=crlf -------------------------------------------------------------------------------- /src/op-core/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./App"; 2 | export * from "./store"; 3 | -------------------------------------------------------------------------------- /src/op-native/react-native-immersive.ts: -------------------------------------------------------------------------------- 1 | export const Immersive: any = {}; 2 | -------------------------------------------------------------------------------- /src/op-board/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Board"; 2 | export * from "./store"; 3 | -------------------------------------------------------------------------------- /src/op-config/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./constants"; 2 | export * from "./strings"; 3 | -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/public/icon.png -------------------------------------------------------------------------------- /.github/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/.github/logo.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # Ignore CRA warnings on startup caused by misaligned versions of babel 2 | SKIP_PREFLIGHT_CHECK=true -------------------------------------------------------------------------------- /public/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/public/icon-192.png -------------------------------------------------------------------------------- /public/icon-2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/public/icon-2048.png -------------------------------------------------------------------------------- /public/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/public/icon-512.png -------------------------------------------------------------------------------- /src/op-utils/delay.ts: -------------------------------------------------------------------------------- 1 | export const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); 2 | -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/assets/images/icon.png -------------------------------------------------------------------------------- /src/op-native/react-native-keep-awake.ts: -------------------------------------------------------------------------------- 1 | import { Fragment } from "react"; 2 | 3 | export default Fragment; 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app", "plugin:prettier/recommended"], 3 | "plugins": ["prettier"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/web-app-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/.github/web-app-badge.png -------------------------------------------------------------------------------- /src/op-native/react-native-bootsplash.ts: -------------------------------------------------------------------------------- 1 | const mock = { 2 | hide: () => {}, 3 | }; 4 | 5 | export default mock; 6 | -------------------------------------------------------------------------------- /src/op-native/react-native-sound.native.ts: -------------------------------------------------------------------------------- 1 | import Sound from "react-native-sound"; 2 | 3 | export default Sound; 4 | -------------------------------------------------------------------------------- /.github/app-store-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/.github/app-store-badge.png -------------------------------------------------------------------------------- /.github/play-store-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/.github/play-store-badge.png -------------------------------------------------------------------------------- /android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/debug.keystore -------------------------------------------------------------------------------- /assets/audio/buttonpress.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/assets/audio/buttonpress.wav -------------------------------------------------------------------------------- /assets/fonts/Inter-SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/assets/fonts/Inter-SemiBold.otf -------------------------------------------------------------------------------- /src/op-home/logo-border-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/src/op-home/logo-border-dark.png -------------------------------------------------------------------------------- /src/op-home/logo-border-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/src/op-home/logo-border-light.png -------------------------------------------------------------------------------- /.github/iphone-screenshot-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/.github/iphone-screenshot-dark.png -------------------------------------------------------------------------------- /.github/iphone-screenshot-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/.github/iphone-screenshot-light.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Ordinary Puzzles 3 | 4 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/op-native/react-native-bootsplash.native.ts: -------------------------------------------------------------------------------- 1 | import RNBootSplash from "react-native-bootsplash"; 2 | export default RNBootSplash; 3 | -------------------------------------------------------------------------------- /src/op-native/react-native-keep-awake.native.ts: -------------------------------------------------------------------------------- 1 | import KeepAwake from "react-native-keep-awake"; 2 | 3 | export default KeepAwake; 4 | -------------------------------------------------------------------------------- /android/app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "react-native", 3 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 4 | }; 5 | -------------------------------------------------------------------------------- /src/op-design/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./animations"; 2 | export * from "./colors"; 3 | export * from "./fonts"; 4 | export * from "./metrics"; 5 | -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | project: { 3 | ios: {}, 4 | android: {}, 5 | }, 6 | assets: ["./assets/fonts/"], 7 | }; 8 | -------------------------------------------------------------------------------- /src/op-design/metrics.ts: -------------------------------------------------------------------------------- 1 | export const metrics = { 2 | screenMargin: 16, 3 | webMaxLayoutWidth: 1024, 4 | webBoardMaxLayoutWidth: 800, 5 | }; 6 | -------------------------------------------------------------------------------- /ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // OrdinaryPuzzles 4 | // 5 | // Created by Matteo Mazzarolo on 11/10/2020. 6 | // 7 | 8 | import Foundation 9 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #84818D` 4 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles-tvOS-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/assets/fonts/Inter-SemiBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/assets/fonts/Inter-SemiBold.otf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/index.native.ts: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from "react-native"; 2 | import { App } from "op-core"; 3 | 4 | AppRegistry.registerComponent("OrdinaryPuzzles", () => App); 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src/op-config/strings.ts: -------------------------------------------------------------------------------- 1 | export const credits = [ 2 | "game design by\nMatteo Mazzarolo", 3 | "puzzles design by\nJuho Snellman", 4 | "font design by\nKostas Bartsokas", 5 | ]; 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src/op-native/react-native-immersive.native.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import * as immersive from "react-native-immersive"; 3 | 4 | // @ts-ignore 5 | export const Immersive = immersive.Immersive; 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_app76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_app76x76.png -------------------------------------------------------------------------------- /src/op-common/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./AnimatedLetter"; 2 | export * from "./BottomNav"; 3 | export * from "./Button"; 4 | export * from "./Header"; 5 | export * from "./Score"; 6 | export * from "./Text"; 7 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_app76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_app76x76@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_settings29x29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_settings29x29.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_spotlight40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_spotlight40x40.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_app60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_app60x60@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_app60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_app60x60@3x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_settings29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_settings29x29@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ios_marketing1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ios_marketing1024x1024.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_notification20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_notification20x20.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_pro_app83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_pro_app83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_spotlight40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_spotlight40x40@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_settings29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_settings29x29@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_settings29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_settings29x29@3x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_spotlight40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_spotlight40x40@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_spotlight40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_spotlight40x40@3x.png -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'OrdinaryPuzzles' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_notification20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/ipad_notification20x20@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_notification20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_notification20x20@2x.png -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_notification20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mmazzarolo/ordinary-puzzles-app/HEAD/ios/OrdinaryPuzzles/Images.xcassets/AppIcon.appiconset/iphone_notification20x20@3x.png -------------------------------------------------------------------------------- /src/op-native/react-native-sound.ts: -------------------------------------------------------------------------------- 1 | export default class MockWebSound { 2 | constructor(a: any, b: any, c: (a: any) => void) {} // eslint-disable-line 3 | static setCategory(a: string, b: boolean) {} 4 | static MAIN_BUNDLE = ""; 5 | } 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/bootsplash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/op-utils/hapticFeedback.ts: -------------------------------------------------------------------------------- 1 | import ReactNativeHaptic from "react-native-haptic"; 2 | import { Platform } from "react-native"; 3 | 4 | export const hapticFeedback = { 5 | generate: 6 | Platform.OS === "ios" ? ReactNativeHaptic.generate : () => undefined, 7 | }; 8 | -------------------------------------------------------------------------------- /src/op-utils/useOnMount.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | type EffectCallback = () => void | (() => void); 4 | 5 | export function useOnMount(onMount: EffectCallback) { 6 | // TODO: re-think this 7 | useEffect(onMount, []); // eslint-disable-line 8 | } 9 | -------------------------------------------------------------------------------- /src/op-utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./delay"; 2 | export * from "./hapticFeedback"; 3 | export * from "./pickPuzzle"; 4 | export * from "./scale"; 5 | export * from "./sound"; 6 | export * from "./storage"; 7 | export * from "./useAnimation"; 8 | export * from "./useHardwareBackButton"; 9 | export * from "./useOnMount"; 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /src/op-splash/Splash.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from "react"; 2 | import { useOnMount } from "op-utils"; 3 | 4 | interface SplashProps { 5 | onHide: () => void; 6 | } 7 | 8 | export const Splash: FC = function ({ onHide }) { 9 | // @ts-ignore 10 | document.getElementById("splash").style.display = "none"; 11 | 12 | useOnMount(() => { 13 | onHide(); 14 | }); 15 | 16 | return null; 17 | }; 18 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.autoFixOnSave": true, 3 | "editor.formatOnSave": true, 4 | "eslint.enable": true, 5 | "eslint.validate": [ 6 | "javascript", 7 | "javascriptreact", 8 | "typescript", 9 | "typescriptreact" 10 | ], 11 | "typescript.preferences.importModuleSpecifier": "auto", 12 | "typescript.tsdk": "node_modules/typescript/lib", 13 | "editor.codeActionsOnSave": { 14 | "source.fixAll.eslint": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["module:metro-react-native-babel-preset"], 3 | plugins: [ 4 | [ 5 | "module-resolver", 6 | { 7 | root: ["./src"], 8 | extensions: [".ios.js", ".android.js", ".js", ".ts", ".tsx", ".json"], 9 | alias: { "test/*": "./test/" }, 10 | }, 11 | ], 12 | ], 13 | env: { 14 | production: { 15 | plugins: ["transform-remove-console"], 16 | }, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Ordinary Puzzles", 3 | "name": "Ordinary Puzzles", 4 | "description": "Minimalistic logic puzzle game", 5 | "icons": [ 6 | { 7 | "src": "icon-192.png", 8 | "type": "image/png", 9 | "sizes": "192x192", 10 | "purpose": "any maskable" 11 | }, 12 | { 13 | "src": "icon-512.png", 14 | "type": "image/png", 15 | "sizes": "512x512", 16 | "purpose": "any maskable" 17 | } 18 | ], 19 | "start_url": ".", 20 | "display": "fullscreen", 21 | "theme_color": "#fff", 22 | "background_color": "#FBFAFF" 23 | } 24 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const { override, addWebpackPlugin } = require("customize-cra"); 3 | 4 | // Rename the CRA entry point to avoid confustion between it and the native one. 5 | const paths = require("react-scripts/config/paths"); 6 | paths.appIndexJs = `${paths.appSrc}/index.web.ts`; 7 | 8 | module.exports = override( 9 | addWebpackPlugin( 10 | new webpack.DefinePlugin({ 11 | "process.env.NODE_ENV": JSON.stringify( 12 | process.env.NODE_ENV || "development" 13 | ), 14 | __DEV__: process.env.NODE_ENV !== "production", 15 | __ELECTRON__: !!process.env.ELECTRON, 16 | }) 17 | ) 18 | ); 19 | -------------------------------------------------------------------------------- /src/op-core/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { metrics } from "op-design"; 2 | import React, { FC } from "react"; 3 | import { StyleSheet, Platform, View } from "react-native"; 4 | 5 | // Centers the layout horizontally on the web, clamping it to "webMaxLayoutWidth" 6 | export const Layout: FC = function ({ children }) { 7 | return Platform.select({ 8 | native: <>{children}, 9 | default: {children}, 10 | }); 11 | }; 12 | 13 | const styles = StyleSheet.create({ 14 | root: { 15 | height: "100%", 16 | width: "100%", 17 | maxWidth: metrics.webMaxLayoutWidth, 18 | alignSelf: "center", 19 | flex: 1, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /src/op-utils/useAnimation.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { Animated, Easing } from "react-native"; 3 | 4 | export const useAnimation = function (initialValue: number = 0) { 5 | const endValue = initialValue === 0 ? 1 : 0; 6 | const animationValueRef = useRef(new Animated.Value(initialValue)); 7 | 8 | const setup = (config: Partial = {}) => 9 | Animated.timing(animationValueRef.current, { 10 | toValue: endValue, 11 | useNativeDriver: true, 12 | easing: Easing.inOut(Easing.quad), 13 | ...config, 14 | }); 15 | 16 | return { 17 | value: animationValueRef.current, 18 | setup: setup, 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /src/op-utils/useHardwareBackButton.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { NativeEventSubscription, BackHandler } from "react-native"; 3 | import { useOnMount } from "./useOnMount"; 4 | 5 | export function useHardwareBackButton(onBackButtonPress: () => void) { 6 | const backHandlerRef = useRef(); 7 | useOnMount(() => { 8 | backHandlerRef.current = BackHandler.addEventListener( 9 | "hardwareBackPress", 10 | () => { 11 | onBackButtonPress(); 12 | return true; 13 | } 14 | ); 15 | return () => { 16 | if (backHandlerRef.current) { 17 | backHandlerRef.current.remove(); 18 | } 19 | }; 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "baseUrl": "src", 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "isolatedModules": true, 9 | "jsx": "react", 10 | "lib": ["esnext", "dom"], 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "noEmit": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "resolveJsonModule": true, 16 | "skipLibCheck": true, 17 | "strict": true, 18 | "target": "esnext", 19 | "useDefineForClassFields": true 20 | }, 21 | "exclude": [ 22 | "node_modules", 23 | "babel.config.js", 24 | "metro.config.js", 25 | "jest.config.js" 26 | ], 27 | "include": ["src"] 28 | } 29 | -------------------------------------------------------------------------------- /src/op-design/colors.ts: -------------------------------------------------------------------------------- 1 | import { Appearance, useColorScheme } from "react-native"; 2 | import tinycolor from "tinycolor2"; 3 | 4 | const primaryColor = "#171520"; 5 | const splashColor = "#84818D"; 6 | 7 | const palette = new Array(10).fill(primaryColor).map((color, index) => 8 | tinycolor(color) 9 | .brighten(index * 10) 10 | .toString() 11 | ); 12 | const lightColors = palette; 13 | const darkColors = palette.slice().reverse(); 14 | 15 | export const colors = { 16 | primary: Appearance.getColorScheme() === "dark" ? darkColors : lightColors, 17 | splash: splashColor, 18 | }; 19 | 20 | export const useColors = function () { 21 | const colorScheme = useColorScheme(); 22 | return { 23 | primary: colorScheme === "dark" ? darkColors : lightColors, 24 | splash: splashColor, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /src/index.web.ts: -------------------------------------------------------------------------------- 1 | /* global __ELECTRON__ */ 2 | import { AppRegistry } from "react-native"; 3 | import { App } from "op-core"; 4 | import * as serviceWorkerRegistration from "./serviceWorkerRegistration"; 5 | 6 | AppRegistry.registerComponent("OrdinaryPuzzles", () => App); 7 | 8 | // Load the app only when all the fonts are loaded 9 | Promise.all([ 10 | // @ts-ignore 11 | document.fonts.load("12px Averta-Bold"), 12 | // @ts-ignore 13 | document.fonts.load("12px Averta-Semibold"), 14 | // @ts-ignore 15 | document.fonts.load("12px Averta-Regular"), 16 | ]).then((f) => { 17 | AppRegistry.runApplication("OrdinaryPuzzles", { 18 | rootTag: document.getElementById("root"), 19 | }); 20 | }); 21 | 22 | // Opt-out of the service-worker in Electron 23 | // @ts-ignore 24 | if (!__ELECTRON__) { 25 | serviceWorkerRegistration.register(); 26 | } 27 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzlesTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/OrdinaryPuzzles-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/op-common/AnimatedLetter.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { Animated } from "react-native"; 3 | import { Text, TextProps } from "op-common"; 4 | import { animations } from "op-design"; 5 | import { useScale } from "op-utils"; 6 | 7 | interface AnimatedLetterProps extends TextProps { 8 | animValue: Animated.Value; 9 | delay: number; 10 | value: string; 11 | secondary?: boolean; 12 | } 13 | 14 | export const AnimatedLetter: FC = function ({ 15 | animValue, 16 | delay, 17 | style, 18 | value, 19 | secondary, 20 | }) { 21 | const scale = useScale(); 22 | const charAnimatedStyle = animations.fadeSlideBottom(animValue, scale, { 23 | interpolateStart: delay, 24 | }); 25 | return ( 26 | 31 | {value} 32 | 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /src/op-utils/storage.ts: -------------------------------------------------------------------------------- 1 | import AsyncStorage from "@react-native-community/async-storage"; 2 | 3 | const storageItemKeys = ["completedPuzzles", "playedPuzzles"] as const; 4 | 5 | type ElementType> = T extends ReadonlyArray< 6 | infer ElementType 7 | > 8 | ? ElementType 9 | : never; 10 | 11 | type StorageItemKey = ElementType; 12 | 13 | export const clearStorage = async () => { 14 | await Promise.all(storageItemKeys.map((key) => AsyncStorage.removeItem(key))); 15 | }; 16 | 17 | export const rehydrateObject = async (key: StorageItemKey) => { 18 | const serializedItem = await AsyncStorage.getItem(key); 19 | const item = serializedItem ? JSON.parse(serializedItem) : undefined; 20 | return item; 21 | }; 22 | 23 | export const persistObject = async (key: StorageItemKey, value: Object) => { 24 | const serializedItem = JSON.stringify(value); 25 | await AsyncStorage.setItem(key, serializedItem); 26 | }; 27 | -------------------------------------------------------------------------------- /src/op-core/Main.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState } from "react"; 2 | import { StyleSheet, SafeAreaView } from "react-native"; 3 | import { useColors } from "op-design"; 4 | import { Splash } from "op-splash"; 5 | import { skipSplashScreen } from "op-config"; 6 | import { Router } from "./Router"; 7 | import { Layout } from "./Layout"; 8 | 9 | export const Main: FC = function () { 10 | const colors = useColors(); 11 | const [isShowingSplash, setIsShowingSplash] = useState(!skipSplashScreen); 12 | const hideSplash = () => { 13 | setIsShowingSplash(false); 14 | }; 15 | 16 | if (isShowingSplash) return ; 17 | 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | }; 26 | 27 | const styles = StyleSheet.create({ 28 | root: { 29 | height: "100%", 30 | width: "100%", 31 | flex: 1, 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /src/op-config/constants.ts: -------------------------------------------------------------------------------- 1 | // Turn "simulateProduction" on to simulate a production environment 2 | const simulateProduction = false; 3 | 4 | // Enable MobX logging (trough the mobx-logger lib) 5 | const _enableMobxLogging = true; 6 | 7 | // Don't show the splash screen 8 | const _skipSplashScreen = false; 9 | 10 | // Clean the local-storage 11 | const _simulateFirstLoad = false; 12 | 13 | // Auto-solve the puzzle after 2000 ms 14 | const _autoSolve = false; 15 | 16 | // Use the Averta font? 17 | const _useAvertaFont = true; 18 | 19 | // To be safe, let's make sure we don't user development settings in production 20 | const isDevelopment = __DEV__ && !simulateProduction; 21 | export const enableMobxLogging = isDevelopment && _enableMobxLogging; 22 | export const skipSplashScreen = isDevelopment && _skipSplashScreen; 23 | export const simulateFirstLoad = isDevelopment && _simulateFirstLoad; 24 | export const autoSolve = isDevelopment && _autoSolve; 25 | export const useAvertaFont = !isDevelopment || _useAvertaFont; 26 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'OrdinaryPuzzles' do 7 | config = use_native_modules! 8 | 9 | use_react_native!(:path => config["reactNativePath"]) 10 | 11 | target 'OrdinaryPuzzlesTests' do 12 | inherit! :search_paths 13 | # Pods for testing 14 | end 15 | 16 | # Enables Flipper. 17 | # 18 | # Note that if you have use_frameworks! enabled, Flipper will not work and 19 | # you should disable these next few lines. 20 | use_flipper!({ 'Flipper-Folly' => '2.5.3', 'Flipper' => '0.87.0', 'Flipper-RSocket' => '1.3.1' }) 21 | post_install do |installer| 22 | flipper_post_install(installer) 23 | end 24 | end 25 | 26 | target 'OrdinaryPuzzles-tvOS' do 27 | # Pods for OrdinaryPuzzles-tvOS 28 | 29 | target 'OrdinaryPuzzles-tvOSTests' do 30 | inherit! :search_paths 31 | # Pods for testing 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /src/op-common/Text.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { TextProps as RNTextProps, StyleSheet, Animated } from "react-native"; 3 | import { fonts, useColors } from "op-design"; 4 | 5 | export type TextFamily = keyof typeof fonts; 6 | 7 | export type TextWeight = keyof typeof fonts.primary; 8 | 9 | export interface TextProps extends RNTextProps { 10 | family?: TextFamily; 11 | weight?: TextWeight; 12 | secondary?: boolean; 13 | style?: any; // Because on the missing "Animated" typings for the style 14 | } 15 | 16 | export const Text: FC = function ({ 17 | children, 18 | family = "primary", 19 | weight = "regular", 20 | secondary = false, 21 | style, 22 | }) { 23 | const colors = useColors(); 24 | const font = fonts[family][weight]; 25 | const color = secondary ? colors.primary[5] : colors.primary[0]; 26 | return ( 27 | 28 | {children} 29 | 30 | ); 31 | }; 32 | 33 | const styles = StyleSheet.create({ 34 | text: {}, 35 | }); 36 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/mmazzarolo/ordinarypuzzles/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.mmazzarolo.ordinarypuzzles; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.facebook.react.ReactActivity; 6 | import com.zoontek.rnbootsplash.RNBootSplash; 7 | import com.rnimmersive.RNImmersiveModule; 8 | 9 | public class MainActivity extends ReactActivity { 10 | 11 | /** 12 | * Returns the name of the main component registered from JavaScript. This is used to schedule 13 | * rendering of the component. 14 | */ 15 | @Override 16 | protected String getMainComponentName() { 17 | return "OrdinaryPuzzles"; 18 | } 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | RNBootSplash.show(R.drawable.bootsplash, MainActivity.this); 24 | } 25 | 26 | @Override 27 | public void onWindowFocusChanged(boolean hasFocus) { 28 | super.onWindowFocusChanged(hasFocus); 29 | if (hasFocus && RNImmersiveModule.getInstance() != null) { 30 | RNImmersiveModule.getInstance().emitImmersiveStateChangeEvent(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Matteo Mazzarolo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle:3.5.3") 16 | 17 | // NOTE: Do not place your application dependencies here; they belong 18 | // in the individual module build.gradle files 19 | } 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | mavenLocal() 25 | maven { 26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 27 | url("$rootDir/../node_modules/react-native/android") 28 | } 29 | maven { 30 | // Android JSC is installed from npm 31 | url("$rootDir/../node_modules/jsc-android/dist") 32 | } 33 | 34 | google() 35 | jcenter() 36 | maven { url 'https://www.jitpack.io' } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/op-common/BottomNav.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { Animated } from "react-native"; 3 | import { animations } from "op-design"; 4 | import { useScale, ScalingFunc } from "op-utils"; 5 | import { defaultButtonTextSize } from "./Button"; 6 | 7 | interface BottomNavProps { 8 | animValue: Animated.Value; 9 | } 10 | 11 | export const BottomNav: FC = function ({ 12 | animValue, 13 | children, 14 | }) { 15 | const scale = useScale(); 16 | const styles = createStyles({ scale }); 17 | return ( 18 | 21 | {children} 22 | 23 | ); 24 | }; 25 | 26 | const marginTop = 10; 27 | const marginBottom = 14; 28 | 29 | const createStyles = ({ scale }: { scale: ScalingFunc }): any => ({ 30 | root: { 31 | flexDirection: "row", 32 | justifyContent: "space-between", 33 | marginTop: scale(marginTop), 34 | marginBottom: scale(marginBottom), 35 | }, 36 | }); 37 | 38 | export const getBottomNavHeight = (scale: ScalingFunc): any => 39 | scale(marginTop) + scale(marginBottom) + scale(defaultButtonTextSize); 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | 61 | # Fonts 62 | Averta-Bold.* 63 | Averta-Regular.* 64 | Averta-Semibold.* 65 | 66 | # Electron outputs 67 | dist/ -------------------------------------------------------------------------------- /src/op-core/Router.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { observer } from "mobx-react"; 3 | import { Home } from "op-home/Home"; 4 | import { Game } from "op-game/Game"; 5 | import { Intro } from "op-intro/Intro"; 6 | import { Tutorial } from "op-tutorial/Tutorial"; 7 | import { Message } from "op-message/Message"; 8 | import { Success } from "op-success/Success"; 9 | import { Stats } from "op-stats/Stats"; 10 | import { useCoreStores } from "./store"; 11 | 12 | export const Router: FC = observer(function () { 13 | const { puzzle, router } = useCoreStores(); 14 | if (router.currentRoute === "home") { 15 | return ; 16 | } else if (router.currentRoute === "intro") { 17 | return ; 18 | } else if (router.currentRoute === "game") { 19 | return ; 20 | } else if (router.currentRoute === "success") { 21 | return ; 22 | } else if (router.currentRoute === "stats") { 23 | return ; 24 | } else if (router.currentRoute === "tutorial") { 25 | if (puzzle.type === "message") { 26 | return ; 27 | } else { 28 | return ; 29 | } 30 | } else { 31 | throw new Error(`Invalid route: ${router.currentRoute}`); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /src/op-tutorial/Description.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { Animated, ViewProps } from "react-native"; 3 | import { Text } from "op-common"; 4 | import { animations } from "op-design"; 5 | import { useScale, ScalingFunc } from "op-utils"; 6 | 7 | interface DescriptionProps extends ViewProps { 8 | animValue: Animated.Value; 9 | title: string; 10 | message: string; 11 | } 12 | 13 | export const Description: FC = function ({ 14 | animValue, 15 | title, 16 | message, 17 | ...otherProps 18 | }) { 19 | const scale = useScale(); 20 | const styles = createStyles({ scale }); 21 | return ( 22 | 26 | 27 | {title} 28 | 29 | 30 | {message} 31 | 32 | 33 | ); 34 | }; 35 | 36 | const createStyles = ({ scale }: { scale: ScalingFunc }): any => ({ 37 | root: { 38 | marginTop: scale(32), 39 | }, 40 | title: { 41 | fontSize: scale(24), 42 | marginBottom: scale(4), 43 | }, 44 | message: { 45 | fontSize: scale(22), 46 | }, 47 | }); 48 | -------------------------------------------------------------------------------- /src/op-design/fonts.ts: -------------------------------------------------------------------------------- 1 | import { Platform } from "react-native"; 2 | import { useAvertaFont } from "op-config"; 3 | 4 | // Font used across the entire app (menu, title, messages, buttons). 5 | // In production it's the "Averta" font. 6 | // I'm not distributing the Averta font on GitHub. 7 | // If you want to contribute, you can toggle off the "useAvertaFont" config 8 | // variable. 9 | const primaryFont = { 10 | regular: { 11 | fontFamily: useAvertaFont ? "Averta-Regular" : undefined, 12 | fontWeight: "400", 13 | }, 14 | semibold: { 15 | fontFamily: useAvertaFont ? "Averta-Semibold" : undefined, 16 | fontWeight: "500", 17 | }, 18 | bold: { 19 | fontFamily: useAvertaFont ? "Averta-Bold" : undefined, 20 | fontWeight: Platform.select({ 21 | native: "600", 22 | default: "500", // On the web this alligns the Safari and Chrome rendering 23 | }), 24 | }, 25 | }; 26 | 27 | // Font used for numbers (just for semibold). 28 | // It's the open-source "Inter" font on Android, "San-Francisco" on iOS. 29 | const secondaryFont = { 30 | semibold: { 31 | fontFamily: Platform.OS === "android" ? "Inter-SemiBold" : undefined, 32 | fontWeight: "600", 33 | }, 34 | bold: {}, 35 | regular: {}, 36 | }; 37 | 38 | export const fonts = { 39 | primary: primaryFont, 40 | secondary: secondaryFont, 41 | }; 42 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.54.0 29 | 30 | ORDINARY_PUZZLES_RELEASE_STORE_FILE=ordinary-puzzles-release-key.keystore 31 | ORDINARY_PUZZLES_RELEASE_KEY_ALIAS=ordinary-puzzles-key-alias -------------------------------------------------------------------------------- /src/op-core/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import { StatusBar, Platform, UIManager } from "react-native"; 3 | import RNBootSplash from "op-native/react-native-bootsplash"; 4 | import { Immersive } from "op-native/react-native-immersive"; 5 | import { configure } from "mobx"; 6 | import { enableLogging } from "mobx-logger"; 7 | import { useOnMount, clearStorage, initializeAudio } from "op-utils"; 8 | import { simulateFirstLoad, enableMobxLogging } from "op-config"; 9 | import { Main } from "./Main"; 10 | import { useCoreStores } from "./store"; 11 | 12 | configure({ 13 | enforceActions: "always", 14 | }); 15 | 16 | if (enableMobxLogging) { 17 | enableLogging({}); 18 | } 19 | 20 | if (Platform.OS === "android") { 21 | Immersive.on(); 22 | Immersive.setImmersive(true); 23 | Immersive.addImmersiveListener(() => Immersive.on()); 24 | if (UIManager.setLayoutAnimationEnabledExperimental) { 25 | UIManager.setLayoutAnimationEnabledExperimental(true); 26 | } 27 | } 28 | 29 | export const App: FC = function () { 30 | const { initializeStore } = useCoreStores(); 31 | const initializeApp = async () => { 32 | if (simulateFirstLoad) { 33 | await clearStorage(); 34 | } 35 | await initializeStore(); 36 | initializeAudio(); 37 | if (Platform.OS === "android" || Platform.OS === "ios") { 38 | RNBootSplash.hide(); 39 | } 40 | }; 41 | useOnMount(() => { 42 | initializeApp(); 43 | }); 44 | return ( 45 | <> 46 |