cleanup
8595
.update_backup/package-lock.json
generated
Normal file
46
.update_backup/package.json
Normal file
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"name": "enrecipes",
|
||||
"version": "1.0.0",
|
||||
"description": "A native application built with NativeScript-Vue",
|
||||
"author": "Vishnu Raghav <design@vishnuraghav.com>",
|
||||
"license": "GPL",
|
||||
"scripts": {
|
||||
"run": "ns run android"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nativescript-community/perms": "^2.1.1",
|
||||
"@nativescript-community/ui-material-activityindicator": "^5.0.30",
|
||||
"@nativescript-community/ui-material-button": "^5.1.0",
|
||||
"@nativescript-community/ui-material-floatingactionbutton": "^5.0.30",
|
||||
"@nativescript-community/ui-material-progress": "^5.0.30",
|
||||
"@nativescript-community/ui-material-ripple": "^5.0.30",
|
||||
"@nativescript/core": "~7.0.0",
|
||||
"@nativescript/imagepicker": "^1.0.0",
|
||||
"@nativescript/social-share": "^2.0.1",
|
||||
"@nativescript/theme": "^3.0.0",
|
||||
"@nativescript/webpack": "3.0.0",
|
||||
"@nativescript/zip": "^5.0.0",
|
||||
"@nstudio/nativescript-checkbox": "^2.0.4",
|
||||
"nativescript-clipboard": "^2.0.0",
|
||||
"nativescript-couchbase-plugin": "^0.9.6",
|
||||
"nativescript-feedback": "^2.0.0",
|
||||
"nativescript-imagecropper": "^4.0.1",
|
||||
"nativescript-plugin-filepicker": "^1.0.0",
|
||||
"nativescript-toast": "^2.0.0",
|
||||
"nativescript-ui-listview": "^9.0.4",
|
||||
"nativescript-ui-sidedrawer": "^9.0.3",
|
||||
"nativescript-vue": "^2.6.1",
|
||||
"vuex": "^3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@nativescript/android": "7.0.1",
|
||||
"@types/node": "^14.0.27",
|
||||
"babel-loader": "^8.1.0",
|
||||
"nativescript-vue-template-compiler": "^2.6.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"vue-loader": "^15.9.1"
|
||||
},
|
||||
"main": "main"
|
||||
}
|
369
.update_backup/webpack.config.js
Normal file
|
@ -0,0 +1,369 @@
|
|||
const { join, relative, resolve, sep } = require("path");
|
||||
|
||||
const webpack = require("webpack");
|
||||
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
||||
const NsVueTemplateCompiler = require("nativescript-vue-template-compiler");
|
||||
|
||||
const nsWebpack = require("@nativescript/webpack");
|
||||
const nativescriptTarget = require("@nativescript/webpack/nativescript-target");
|
||||
const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin");
|
||||
const hashSalt = Date.now().toString();
|
||||
|
||||
module.exports = env => {
|
||||
// Add your custom Activities, Services and other android app components here.
|
||||
const appComponents = env.appComponents || [];
|
||||
appComponents.push(...[
|
||||
"@nativescript/core/ui/frame",
|
||||
"@nativescript/core/ui/frame/activity",
|
||||
]);
|
||||
|
||||
const platform = env && (env.android && "android" || env.ios && "ios" || env.platform);
|
||||
if (!platform) {
|
||||
throw new Error("You need to provide a target platform!");
|
||||
}
|
||||
|
||||
const platforms = ["ios", "android"];
|
||||
const projectRoot = __dirname;
|
||||
|
||||
if (env.platform) {
|
||||
platforms.push(env.platform);
|
||||
}
|
||||
|
||||
// Default destination inside platforms/<platform>/...
|
||||
const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot));
|
||||
|
||||
const {
|
||||
// The 'appPath' and 'appResourcesPath' values are fetched from
|
||||
// the nsconfig.json configuration file.
|
||||
appPath = "app",
|
||||
appResourcesPath = "app/App_Resources",
|
||||
|
||||
// You can provide the following flags when running 'tns run android|ios'
|
||||
snapshot, // --env.snapshot
|
||||
production, // --env.production
|
||||
report, // --env.report
|
||||
hmr, // --env.hmr
|
||||
sourceMap, // --env.sourceMap
|
||||
hiddenSourceMap, // --env.hiddenSourceMap
|
||||
unitTesting, // --env.unitTesting
|
||||
testing, // --env.testing
|
||||
verbose, // --env.verbose
|
||||
snapshotInDocker, // --env.snapshotInDocker
|
||||
skipSnapshotTools, // --env.skipSnapshotTools
|
||||
compileSnapshot // --env.compileSnapshot
|
||||
} = env;
|
||||
|
||||
const useLibs = compileSnapshot;
|
||||
const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap;
|
||||
const externals = nsWebpack.getConvertedExternals(env.externals);
|
||||
|
||||
const mode = production ? "production" : "development"
|
||||
|
||||
const appFullPath = resolve(projectRoot, appPath);
|
||||
const hasRootLevelScopedModules = nsWebpack.hasRootLevelScopedModules({ projectDir: projectRoot });
|
||||
let coreModulesPackageName = "tns-core-modules";
|
||||
const alias = env.alias || {};
|
||||
alias['~/package.json'] = resolve(projectRoot, 'package.json');
|
||||
alias['~'] = appFullPath;
|
||||
alias['@'] = appFullPath;
|
||||
alias['vue'] = 'nativescript-vue';
|
||||
|
||||
if (hasRootLevelScopedModules) {
|
||||
coreModulesPackageName = "@nativescript/core";
|
||||
alias["tns-core-modules"] = coreModulesPackageName;
|
||||
}
|
||||
|
||||
const appResourcesFullPath = resolve(projectRoot, appResourcesPath);
|
||||
|
||||
const copyIgnore = { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] };
|
||||
|
||||
const entryModule = nsWebpack.getEntryModule(appFullPath, platform);
|
||||
const entryPath = `.${sep}${entryModule}`;
|
||||
const entries = env.entries || {};
|
||||
entries.bundle = entryPath;
|
||||
|
||||
const areCoreModulesExternal = Array.isArray(env.externals) && env.externals.some(e => e.indexOf("@nativescript") > -1);
|
||||
if (platform === "ios" && !areCoreModulesExternal && !testing) {
|
||||
entries["tns_modules/@nativescript/core/inspector_modules"] = "inspector_modules";
|
||||
};
|
||||
console.log(`Bundling application for entryPath ${entryPath}...`);
|
||||
|
||||
let sourceMapFilename = nsWebpack.getSourceMapFilename(hiddenSourceMap, __dirname, dist);
|
||||
|
||||
const itemsToClean = [`${dist}/**/*`];
|
||||
if (platform === "android") {
|
||||
itemsToClean.push(`${join(projectRoot, "platforms", "android", "app", "src", "main", "assets", "snapshots")}`);
|
||||
itemsToClean.push(`${join(projectRoot, "platforms", "android", "app", "build", "configurations", "nativescript-android-snapshot")}`);
|
||||
}
|
||||
|
||||
nsWebpack.processAppComponents(appComponents, platform);
|
||||
const config = {
|
||||
mode: mode,
|
||||
context: appFullPath,
|
||||
externals,
|
||||
watchOptions: {
|
||||
ignored: [
|
||||
appResourcesFullPath,
|
||||
// Don't watch hidden files
|
||||
"**/.*",
|
||||
],
|
||||
},
|
||||
target: nativescriptTarget,
|
||||
// target: nativeScriptVueTarget,
|
||||
entry: entries,
|
||||
output: {
|
||||
pathinfo: false,
|
||||
path: dist,
|
||||
sourceMapFilename,
|
||||
libraryTarget: "commonjs2",
|
||||
filename: "[name].js",
|
||||
globalObject: "global",
|
||||
hashSalt
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".vue", ".ts", ".js", ".scss", ".css"],
|
||||
// Resolve {N} system modules from @nativescript/core
|
||||
modules: [
|
||||
resolve(__dirname, `node_modules/${coreModulesPackageName}`),
|
||||
resolve(__dirname, "node_modules"),
|
||||
`node_modules/${coreModulesPackageName}`,
|
||||
"node_modules",
|
||||
],
|
||||
alias,
|
||||
// resolve symlinks to symlinked modules
|
||||
symlinks: true,
|
||||
},
|
||||
resolveLoader: {
|
||||
// don't resolve symlinks to symlinked loaders
|
||||
symlinks: false,
|
||||
},
|
||||
node: {
|
||||
// Disable node shims that conflict with NativeScript
|
||||
"http": false,
|
||||
"timers": false,
|
||||
"setImmediate": false,
|
||||
"fs": "empty",
|
||||
"__dirname": false,
|
||||
},
|
||||
devtool: hiddenSourceMap ? "hidden-source-map" : (sourceMap ? "inline-source-map" : "none"),
|
||||
optimization: {
|
||||
runtimeChunk: "single",
|
||||
noEmitOnErrors: true,
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
vendor: {
|
||||
name: "vendor",
|
||||
chunks: "all",
|
||||
test: (module) => {
|
||||
const moduleName = module.nameForCondition ? module.nameForCondition() : '';
|
||||
return /[\\/]node_modules[\\/]/.test(moduleName) ||
|
||||
appComponents.some(comp => comp === moduleName);
|
||||
|
||||
},
|
||||
enforce: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
minimize: Boolean(production),
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
parallel: true,
|
||||
cache: true,
|
||||
sourceMap: isAnySourceMapEnabled,
|
||||
terserOptions: {
|
||||
output: {
|
||||
comments: false,
|
||||
semicolons: !isAnySourceMapEnabled
|
||||
},
|
||||
compress: {
|
||||
// The Android SBG has problems parsing the output
|
||||
// when these options are enabled
|
||||
'collapse_vars': platform !== "android",
|
||||
sequences: platform !== "android",
|
||||
},
|
||||
keep_fnames: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
include: [join(appFullPath, entryPath + ".js"), join(appFullPath, entryPath + ".ts")],
|
||||
use: [
|
||||
// Require all Android app components
|
||||
platform === "android" && {
|
||||
loader: "@nativescript/webpack/helpers/android-app-components-loader",
|
||||
options: { modules: appComponents },
|
||||
},
|
||||
|
||||
{
|
||||
loader: "@nativescript/webpack/bundle-config-loader",
|
||||
options: {
|
||||
registerPages: true, // applicable only for non-angular apps
|
||||
loadCss: !snapshot, // load the application css if in debug mode
|
||||
unitTesting,
|
||||
appFullPath,
|
||||
projectRoot,
|
||||
ignoredFiles: nsWebpack.getUserDefinedEntries(entries, platform)
|
||||
},
|
||||
},
|
||||
].filter(loader => Boolean(loader)),
|
||||
},
|
||||
{
|
||||
test: /[\/|\\]app\.css$/,
|
||||
use: [
|
||||
'@nativescript/webpack/helpers/style-hot-loader',
|
||||
{
|
||||
loader: "@nativescript/webpack/helpers/css2json-loader",
|
||||
options: { useForImports: true }
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /[\/|\\]app\.scss$/,
|
||||
use: [
|
||||
'@nativescript/webpack/helpers/style-hot-loader',
|
||||
{
|
||||
loader: "@nativescript/webpack/helpers/css2json-loader",
|
||||
options: { useForImports: true }
|
||||
},
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /[\/|\\]app\.css$/,
|
||||
use: [
|
||||
'@nativescript/webpack/helpers/style-hot-loader',
|
||||
'@nativescript/webpack/helpers/apply-css-loader.js',
|
||||
{ loader: "css-loader", options: { url: false } },
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /[\/|\\]app\.scss$/,
|
||||
use: [
|
||||
'@nativescript/webpack/helpers/style-hot-loader',
|
||||
'@nativescript/webpack/helpers/apply-css-loader.js',
|
||||
{ loader: "css-loader", options: { url: false } },
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
appendTsSuffixTo: [/\.vue$/],
|
||||
allowTsInNodeModules: true,
|
||||
compilerOptions: {
|
||||
declaration: false
|
||||
},
|
||||
getCustomTransformers: (program) => ({
|
||||
before: [
|
||||
require("@nativescript/webpack/transformers/ns-transform-native-classes").default
|
||||
]
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: "vue-loader",
|
||||
options: {
|
||||
compiler: NsVueTemplateCompiler,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// ... Vue Loader plugin omitted
|
||||
// make sure to include the plugin!
|
||||
new VueLoaderPlugin(),
|
||||
// Define useful constants like TNS_WEBPACK
|
||||
new webpack.DefinePlugin({
|
||||
"global.TNS_WEBPACK": "true",
|
||||
"global.isAndroid": platform === 'android',
|
||||
"global.isIOS": platform === 'ios',
|
||||
"TNS_ENV": JSON.stringify(mode),
|
||||
"process": "global.process"
|
||||
}),
|
||||
// Remove all files from the out dir.
|
||||
new CleanWebpackPlugin({
|
||||
cleanOnceBeforeBuildPatterns: itemsToClean,
|
||||
verbose: !!verbose
|
||||
}),
|
||||
// Copy assets
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{ from: 'assets/**', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
|
||||
{ from: 'fonts/**', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } },
|
||||
{ from: '**/*.+(jpg|png)', noErrorOnMissing: true, globOptions: { dot: false, ...copyIgnore } }
|
||||
],
|
||||
}),
|
||||
new nsWebpack.GenerateNativeScriptEntryPointsPlugin("bundle"),
|
||||
// For instructions on how to set up workers with webpack
|
||||
// check out https://github.com/nativescript/worker-loader
|
||||
new NativeScriptWorkerPlugin(),
|
||||
new nsWebpack.PlatformFSPlugin({
|
||||
platform,
|
||||
platforms,
|
||||
}),
|
||||
// Does IPC communication with the {N} CLI to notify events when running in watch mode.
|
||||
new nsWebpack.WatchStateLoggerPlugin()
|
||||
],
|
||||
};
|
||||
|
||||
if (unitTesting) {
|
||||
config.module.rules.push(
|
||||
{
|
||||
test: /-page\.js$/,
|
||||
use: "@nativescript/webpack/helpers/script-hot-loader"
|
||||
},
|
||||
{
|
||||
test: /\.(html|xml)$/,
|
||||
use: "@nativescript/webpack/helpers/markup-hot-loader"
|
||||
},
|
||||
|
||||
{ test: /\.(html|xml)$/, use: "@nativescript/webpack/helpers/xml-namespace-loader" }
|
||||
);
|
||||
}
|
||||
|
||||
if (report) {
|
||||
// Generate report files for bundles content
|
||||
config.plugins.push(new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
openAnalyzer: false,
|
||||
generateStatsFile: true,
|
||||
reportFilename: resolve(projectRoot, "report", `report.html`),
|
||||
statsFilename: resolve(projectRoot, "report", `stats.json`),
|
||||
}));
|
||||
}
|
||||
|
||||
if (snapshot) {
|
||||
config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({
|
||||
chunk: "vendor",
|
||||
requireModules: [
|
||||
"@nativescript/core/bundle-entry-points",
|
||||
],
|
||||
projectRoot,
|
||||
webpackConfig: config,
|
||||
snapshotInDocker,
|
||||
skipSnapshotTools,
|
||||
useLibs
|
||||
}));
|
||||
}
|
||||
|
||||
if (hmr) {
|
||||
config.plugins.push(new webpack.HotModuleReplacementPlugin());
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
# NativeScript-Vue Application
|
||||
# EnRecipes
|
||||
|
||||
> A native application built with NativeScript-Vue
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
|
||||
android {
|
||||
defaultConfig {
|
||||
versionCode 2
|
||||
versionCode 1
|
||||
versionName '1.0.0'
|
||||
applicationId 'com.vishnuraghav.enrecipes'
|
||||
minSdkVersion 21
|
||||
generatedDensities = []
|
||||
ndk {
|
||||
abiFilters.clear()
|
||||
abiFilters.addAll(['arm64-v8a','x86'])
|
||||
abiFilters.addAll(['arm64-v8a'])
|
||||
}
|
||||
}
|
||||
aaptOptions {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="__PACKAGE__" android:versionCode="10000" android:versionName="1.0">
|
||||
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
<uses-permission android:name="android.permission.CAMERA" tools:node="remove" />
|
||||
<!-- <uses-feature android:name="android.hardware.camera" /> -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" tools:node="remove" />
|
||||
<uses-permission android:name="android.permission.INTERNET" tools:node="remove" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove" />
|
||||
<application android:name="com.tns.NativeScriptApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:hardwareAccelerated="true" android:largeHeap="true" android:theme="@style/AppTheme" android:requestLegacyExternalStorage="true">
|
||||
<activity android:name="com.tns.NativeScriptActivity" android:label="@string/title_activity_kimera" android:screenOrientation="portrait" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode" android:theme="@style/LaunchScreenTheme" android:windowSoftInputMode="adjustPan">
|
||||
<activity android:name="com.tns.NativeScriptActivity" android:label="@string/title_activity_kimera" android:screenOrientation="portrait" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode" android:theme="@style/LaunchScreenTheme" android:windowSoftInputMode="adjustResize">
|
||||
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
|
BIN
app/App_Resources/Android/src/main/res/drawable-hdpi/camera.png
Normal file
After Width: | Height: | Size: 687 B |
BIN
app/App_Resources/Android/src/main/res/drawable-hdpi/check.png
Normal file
After Width: | Height: | Size: 587 B |
BIN
app/App_Resources/Android/src/main/res/drawable-hdpi/detail.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
app/App_Resources/Android/src/main/res/drawable-hdpi/photo.png
Normal file
After Width: | Height: | Size: 517 B |
BIN
app/App_Resources/Android/src/main/res/drawable-hdpi/plus.png
Normal file
After Width: | Height: | Size: 229 B |
BIN
app/App_Resources/Android/src/main/res/drawable-hdpi/share.png
Normal file
After Width: | Height: | Size: 519 B |
BIN
app/App_Resources/Android/src/main/res/drawable-ldpi/camera.png
Normal file
After Width: | Height: | Size: 390 B |
BIN
app/App_Resources/Android/src/main/res/drawable-ldpi/check.png
Normal file
After Width: | Height: | Size: 341 B |
BIN
app/App_Resources/Android/src/main/res/drawable-ldpi/detail.png
Normal file
After Width: | Height: | Size: 240 B |
BIN
app/App_Resources/Android/src/main/res/drawable-ldpi/photo.png
Normal file
After Width: | Height: | Size: 310 B |
BIN
app/App_Resources/Android/src/main/res/drawable-ldpi/plus.png
Normal file
After Width: | Height: | Size: 173 B |
BIN
app/App_Resources/Android/src/main/res/drawable-ldpi/share.png
Normal file
After Width: | Height: | Size: 312 B |
BIN
app/App_Resources/Android/src/main/res/drawable-mdpi/camera.png
Normal file
After Width: | Height: | Size: 357 B |
BIN
app/App_Resources/Android/src/main/res/drawable-mdpi/check.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
app/App_Resources/Android/src/main/res/drawable-mdpi/detail.png
Normal file
After Width: | Height: | Size: 226 B |
BIN
app/App_Resources/Android/src/main/res/drawable-mdpi/photo.png
Normal file
After Width: | Height: | Size: 300 B |
BIN
app/App_Resources/Android/src/main/res/drawable-mdpi/plus.png
Normal file
After Width: | Height: | Size: 165 B |
BIN
app/App_Resources/Android/src/main/res/drawable-mdpi/share.png
Normal file
After Width: | Height: | Size: 341 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xhdpi/camera.png
Normal file
After Width: | Height: | Size: 704 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xhdpi/check.png
Normal file
After Width: | Height: | Size: 573 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xhdpi/detail.png
Normal file
After Width: | Height: | Size: 328 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xhdpi/photo.png
Normal file
After Width: | Height: | Size: 503 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xhdpi/plus.png
Normal file
After Width: | Height: | Size: 199 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xhdpi/share.png
Normal file
After Width: | Height: | Size: 550 B |
After Width: | Height: | Size: 1.2 KiB |
BIN
app/App_Resources/Android/src/main/res/drawable-xxhdpi/check.png
Normal file
After Width: | Height: | Size: 969 B |
After Width: | Height: | Size: 515 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xxhdpi/photo.png
Normal file
After Width: | Height: | Size: 824 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xxhdpi/plus.png
Normal file
After Width: | Height: | Size: 250 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xxhdpi/share.png
Normal file
After Width: | Height: | Size: 930 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1 KiB |
After Width: | Height: | Size: 604 B |
After Width: | Height: | Size: 882 B |
BIN
app/App_Resources/Android/src/main/res/drawable-xxxhdpi/plus.png
Normal file
After Width: | Height: | Size: 295 B |
After Width: | Height: | Size: 928 B |
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ns_primary">
|
||||
#fafafa
|
||||
#f1f3f5
|
||||
</color>
|
||||
<color name="ns_primaryDark">
|
||||
#ff5200
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- theme to use FOR launch screen -->
|
||||
<style name="LaunchScreenThemeBase" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<style name="LaunchScreenThemeBase" parent="Theme.MaterialComponents.NoActionBar">
|
||||
<item name="toolbarStyle">
|
||||
@style/NativeScriptToolbarStyle
|
||||
</item>
|
||||
|
@ -27,7 +27,10 @@
|
|||
<style name="LaunchScreenTheme" parent="LaunchScreenThemeBase">
|
||||
</style>
|
||||
<!-- theme to use AFTER launch screen is loaded -->
|
||||
<style name="AppThemeBase" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<style name="AppThemeBase" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||
<item name="searchViewStyle">
|
||||
@style/SearchViewMy
|
||||
</item>
|
||||
<item name="toolbarStyle">
|
||||
@style/NativeScriptToolbarStyle
|
||||
</item>
|
||||
|
@ -40,21 +43,42 @@
|
|||
<item name="colorAccent">
|
||||
@color/ns_accent
|
||||
</item>
|
||||
<item name="colorControlNormal">
|
||||
#868e96
|
||||
</item>
|
||||
</style>
|
||||
<style name="AppTheme" parent="AppThemeBase">
|
||||
</style>
|
||||
<!-- theme for action-bar -->
|
||||
<style name="NativeScriptToolbarStyleBase" parent="Widget.AppCompat.Toolbar">
|
||||
<style name="NativeScriptToolbarStyleBase" parent="Widget.MaterialComponents.Toolbar">
|
||||
<item name="contentInsetStart">
|
||||
0dp
|
||||
</item>
|
||||
<item name="contentInsetEnd">
|
||||
0dp
|
||||
</item>
|
||||
<item name="android:divider">
|
||||
#ff0000
|
||||
</item>
|
||||
<item name="android:background">
|
||||
@color/ns_primary
|
||||
</item>
|
||||
<item name="theme">
|
||||
@style/ThemeOverlay.AppCompat.ActionBar
|
||||
@style/ThemeOverlay.MaterialComponents.ActionBar
|
||||
</item>
|
||||
<item name="popupTheme">
|
||||
@style/ThemeOverlay.AppCompat
|
||||
@style/ThemeOverlay.MaterialComponents
|
||||
</item>
|
||||
</style>
|
||||
<style name="NativeScriptToolbarStyle" parent="NativeScriptToolbarStyleBase">
|
||||
</style>
|
||||
<!-- theme for searchview -->
|
||||
<style name="SearchViewMy" parent="Widget.AppCompat.Light.SearchView">
|
||||
<item name="searchHintIcon">
|
||||
@null
|
||||
</item>
|
||||
<!-- <item name="closeIcon">
|
||||
@null
|
||||
</item> -->
|
||||
</style>
|
||||
</resources>
|
||||
|
|
292
app/app.scss
|
@ -1,27 +1,24 @@
|
|||
// NativeScript core theme
|
||||
// @see https://docs.nativescript.org/ui/theme
|
||||
@import "~@nativescript/theme/core";
|
||||
|
||||
// Override variables here
|
||||
|
||||
$grayD4: #212121;
|
||||
$grayD3: #424242;
|
||||
$grayD2: #616161;
|
||||
$grayD1: #757575;
|
||||
$gray: #9e9e9e;
|
||||
$grayL1: #e0e0e0;
|
||||
$grayL2: #eeeeee;
|
||||
$grayL3: #f5f5f5;
|
||||
$grayL4: #fafafa;
|
||||
$gray1: #f1f3f5;
|
||||
$gray2: #e9ecef;
|
||||
$gray3: #dee2e6;
|
||||
$gray4: #ced4da;
|
||||
$gray5: #adb5bd; // rgb(173,181,189)
|
||||
$gray6: #868e96;
|
||||
$gray7: #495057;
|
||||
$gray8: #343a40;
|
||||
$gray9: #212529;
|
||||
$orange: #ff5200;
|
||||
|
||||
$fabRipple: #ff864d;
|
||||
$red: #c92a2a;
|
||||
// Global SCSS styling
|
||||
// @see https://docs.nativescript.org/ui/styling
|
||||
|
||||
// * {
|
||||
// font-size: 16;
|
||||
// }
|
||||
Page {
|
||||
Page,
|
||||
.ns-modal {
|
||||
font-family: "Orkney-Regular";
|
||||
}
|
||||
.orkm {
|
||||
|
@ -36,26 +33,30 @@ Page {
|
|||
font-size: 16;
|
||||
}
|
||||
}
|
||||
|
||||
.ns-light {
|
||||
Page,
|
||||
ActionBar,
|
||||
SearchBar,
|
||||
TabView,
|
||||
ListPicker {
|
||||
color: $grayD4;
|
||||
background: $grayL4;
|
||||
color: $gray9;
|
||||
background: $gray1;
|
||||
}
|
||||
MDRipple,
|
||||
MDButton {
|
||||
ripple-color: rgba($gray6, 0.2);
|
||||
}
|
||||
|
||||
TabView {
|
||||
tab-background-color: $grayL4;
|
||||
selected-tab-text-color: $grayD4;
|
||||
tab-background-color: $gray1;
|
||||
selected-tab-text-color: $gray9;
|
||||
}
|
||||
.hr {
|
||||
border-color: $grayL2;
|
||||
border-color: $gray3;
|
||||
}
|
||||
.sd,
|
||||
.fieldLabel {
|
||||
background: $grayL4;
|
||||
background: $gray1;
|
||||
}
|
||||
.referenceItem,
|
||||
.recipeText,
|
||||
|
@ -63,36 +64,32 @@ Page {
|
|||
.recipeItem {
|
||||
background: white;
|
||||
}
|
||||
.option-highlight,
|
||||
.selected-sd-item {
|
||||
background: $grayL2;
|
||||
}
|
||||
|
||||
.sd-item,
|
||||
.sd-group-header,
|
||||
.time .bx {
|
||||
color: $grayD2;
|
||||
color: $gray8;
|
||||
}
|
||||
.fab-button {
|
||||
color: white;
|
||||
background-color: $orange;
|
||||
}
|
||||
.option,
|
||||
.icon-option {
|
||||
.bx,
|
||||
.option-info {
|
||||
color: $grayD2;
|
||||
}
|
||||
.option .bx,
|
||||
.option .info {
|
||||
color: $gray7;
|
||||
}
|
||||
.imageHolder {
|
||||
color: $gray;
|
||||
background: $grayL2;
|
||||
color: $gray4;
|
||||
background: $gray3;
|
||||
}
|
||||
.count {
|
||||
color: $grayL4;
|
||||
background: $grayD4;
|
||||
color: $gray1;
|
||||
background: $gray9;
|
||||
}
|
||||
.instruction {
|
||||
border-color: $grayD4;
|
||||
border-color: $gray9;
|
||||
}
|
||||
MDProgress {
|
||||
progress-background-color: $gray4;
|
||||
}
|
||||
MDFloatingActionButton {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,56 +99,56 @@ Page {
|
|||
SearchBar,
|
||||
TabView,
|
||||
ListPicker {
|
||||
color: $grayL4;
|
||||
background: $grayD4;
|
||||
color: $gray1;
|
||||
background: $gray9;
|
||||
}
|
||||
TabView {
|
||||
tab-background-color: $grayD4;
|
||||
selected-tab-text-color: $grayL4;
|
||||
tab-background-color: $gray9;
|
||||
selected-tab-text-color: $gray1;
|
||||
}
|
||||
MDRipple,
|
||||
MDButton {
|
||||
ripple-color: rgba($gray4, 0.1);
|
||||
}
|
||||
|
||||
.hr {
|
||||
border-color: #111;
|
||||
}
|
||||
.sd,
|
||||
.fieldLabel {
|
||||
background: $grayD4;
|
||||
background: $gray9;
|
||||
}
|
||||
.referenceItem,
|
||||
.recipeText,
|
||||
.overviewItem,
|
||||
.recipeItem,
|
||||
.option-highlight {
|
||||
background: $grayD3;
|
||||
.recipeItem {
|
||||
background: $gray8;
|
||||
}
|
||||
.sd-item,
|
||||
.sd-group-header,
|
||||
.time .bx {
|
||||
color: $grayL2;
|
||||
color: $gray3;
|
||||
}
|
||||
.selected-sd-item {
|
||||
background: #111;
|
||||
}
|
||||
.fab-button {
|
||||
color: #111;
|
||||
background: $orange;
|
||||
}
|
||||
.option,
|
||||
.icon-option {
|
||||
.bx,
|
||||
.option-info {
|
||||
color: $gray;
|
||||
}
|
||||
.option .bx,
|
||||
.option .info {
|
||||
color: $gray5;
|
||||
}
|
||||
.imageHolder {
|
||||
color: $gray;
|
||||
color: $gray8;
|
||||
background: #111;
|
||||
}
|
||||
.count {
|
||||
color: $grayD4;
|
||||
background: $grayL4;
|
||||
color: $gray9;
|
||||
background: $gray1;
|
||||
}
|
||||
.instruction {
|
||||
border-color: $grayL4;
|
||||
border-color: $gray1;
|
||||
}
|
||||
MDProgress {
|
||||
progress-background-color: $gray6;
|
||||
}
|
||||
MDFloatingActionButton {
|
||||
color: $gray9;
|
||||
}
|
||||
}
|
||||
// -----------------------------
|
||||
|
@ -165,48 +162,56 @@ TimePickerField {
|
|||
padding: 14 14 13;
|
||||
margin: 8 0 0 0;
|
||||
border-radius: 4;
|
||||
border-color: $gray;
|
||||
placeholder-color: $gray;
|
||||
border-color: $gray6;
|
||||
placeholder-color: $gray6;
|
||||
}
|
||||
TextView {
|
||||
line-height: 12;
|
||||
}
|
||||
SearchBar {
|
||||
font-family: "Orkney-Regular";
|
||||
font-size: 16;
|
||||
text-field-hint-color: $gray6;
|
||||
}
|
||||
TabView {
|
||||
tab-text-color: $gray;
|
||||
tab-text-color: $gray6;
|
||||
}
|
||||
.inputField {
|
||||
margin-top: 16;
|
||||
margin-bottom: 14;
|
||||
}
|
||||
.fieldLabel {
|
||||
font-size: 12;
|
||||
margin-left: 8;
|
||||
padding: 0 8;
|
||||
margin-left: 12;
|
||||
padding: 0 4;
|
||||
}
|
||||
// prettier-ignore
|
||||
.progressContainer{
|
||||
.progressContainer {
|
||||
width: 100%;
|
||||
}
|
||||
// prettier-ignore
|
||||
.text-btn,
|
||||
.group-header,
|
||||
.category,
|
||||
ActivityIndicator,
|
||||
Progress {
|
||||
MDActivityIndicator {
|
||||
color: $orange;
|
||||
}
|
||||
MDProgress {
|
||||
progress-color: $orange;
|
||||
}
|
||||
// -----------------------------
|
||||
// ActionBar
|
||||
ActionBar {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: 0 8;
|
||||
height: 64;
|
||||
.bx {
|
||||
padding: 16 16 16 4;
|
||||
GridLayout {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
MDButton.bx {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
.title {
|
||||
padding-left: 8;
|
||||
padding-left: 12;
|
||||
text-align: left;
|
||||
font-size: 18;
|
||||
}
|
||||
|
@ -218,7 +223,6 @@ ActionBar {
|
|||
}
|
||||
.sd-item {
|
||||
border-radius: 4;
|
||||
padding: 0 16;
|
||||
height: 48;
|
||||
vertical-alignment: center;
|
||||
.bx {
|
||||
|
@ -226,25 +230,50 @@ ActionBar {
|
|||
}
|
||||
&.selected-sd-item {
|
||||
color: $orange;
|
||||
background: rgba($orange, 0.1);
|
||||
MDRipple {
|
||||
ripple-color: transparent;
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
Label {
|
||||
padding: 0 16 0 0;
|
||||
font-size: 16;
|
||||
vertical-alignment: center;
|
||||
&.bx{
|
||||
padding: 0 0 0 16;
|
||||
}
|
||||
}
|
||||
MDRipple {
|
||||
padding: 0 16;
|
||||
}
|
||||
}
|
||||
.sd-group-header {
|
||||
width: 100%;
|
||||
padding: 8 8 16;
|
||||
padding: 8 0 8 8;
|
||||
}
|
||||
MDRipple {
|
||||
border-radius: 4;
|
||||
}
|
||||
MDButton {
|
||||
padding: 8;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
&.bx {
|
||||
padding: 0;
|
||||
width: 48;
|
||||
height: 48;
|
||||
margin: 0 8 0 0;
|
||||
border-radius: 99;
|
||||
}
|
||||
}
|
||||
// -----------------------------
|
||||
// HOME
|
||||
|
||||
.emptyState {
|
||||
.emptyStateContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.noResult {
|
||||
.emptyState {
|
||||
line-height: 8;
|
||||
padding: 0 32;
|
||||
text-align: center;
|
||||
|
@ -253,7 +282,7 @@ ActionBar {
|
|||
.icon {
|
||||
font-size: 64;
|
||||
text-align: center;
|
||||
color: $gray;
|
||||
color: $gray5;
|
||||
margin-bottom: 16;
|
||||
}
|
||||
.logo {
|
||||
|
@ -271,6 +300,9 @@ ActionBar {
|
|||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
&.noResult {
|
||||
margin-top: 32;
|
||||
}
|
||||
}
|
||||
// -----------------------------
|
||||
// Recipe Items
|
||||
|
@ -313,7 +345,7 @@ RadListView {
|
|||
}
|
||||
.swipe-item {
|
||||
margin: 0 8;
|
||||
background: #c62828;
|
||||
background: $red;
|
||||
color: #fff;
|
||||
height: 128;
|
||||
border-radius: 4;
|
||||
|
@ -324,17 +356,19 @@ RadListView {
|
|||
padding: 8;
|
||||
}
|
||||
.main-container {
|
||||
padding: 16 8 128;
|
||||
padding: 8 8 128;
|
||||
.option {
|
||||
font-size: 16;
|
||||
padding: 16;
|
||||
.bx {
|
||||
margin: 0 24 0 0;
|
||||
margin: 16 24 16 16;
|
||||
}
|
||||
.option-info {
|
||||
.info {
|
||||
font-size: 12;
|
||||
line-height: 4;
|
||||
}
|
||||
StackLayout {
|
||||
margin: 16 24 16 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// -----------------------------
|
||||
|
@ -374,17 +408,16 @@ RadListView {
|
|||
margin-top: 12;
|
||||
.overviewItem {
|
||||
border-radius: 4;
|
||||
padding: 8;
|
||||
margin: 8;
|
||||
android-elevation: 1;
|
||||
.bx {
|
||||
color: $gray;
|
||||
width: 24;
|
||||
padding: 12 0 0 12;
|
||||
color: $gray6;
|
||||
horizontal-alignment: left;
|
||||
}
|
||||
.itemCount {
|
||||
font-size: 16;
|
||||
padding: 8 4 4;
|
||||
padding: 8 12 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +427,7 @@ RadListView {
|
|||
padding-bottom: 12;
|
||||
}
|
||||
.ingredient-check {
|
||||
margin-bottom: 12;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.count {
|
||||
width: 24;
|
||||
|
@ -432,15 +465,14 @@ RadListView {
|
|||
border-width: 0;
|
||||
}
|
||||
.referenceItem {
|
||||
padding: 14 16;
|
||||
margin: 8 16 8;
|
||||
margin: 8 16;
|
||||
border-radius: 4;
|
||||
font-size: 16;
|
||||
.bx {
|
||||
font-size: 24;
|
||||
}
|
||||
.recipeLink {
|
||||
padding: 0 16 0 0;
|
||||
padding: 16;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
@ -448,7 +480,7 @@ RadListView {
|
|||
font-size: 16;
|
||||
line-height: 6;
|
||||
padding: 16;
|
||||
margin: 8 16 8;
|
||||
margin: 8 16;
|
||||
border-radius: 4;
|
||||
}
|
||||
}
|
||||
|
@ -458,14 +490,10 @@ RadListView {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.fab-button {
|
||||
height: 56;
|
||||
width: 56;
|
||||
border-radius: 28;
|
||||
padding: 16;
|
||||
MDFloatingActionButton {
|
||||
margin: 16;
|
||||
vertical-alignment: center;
|
||||
android-elevation: 6;
|
||||
background: $orange;
|
||||
ripple-color: $fabRipple;
|
||||
}
|
||||
// -----------------------------
|
||||
// EDIT RECIPE
|
||||
|
@ -475,51 +503,67 @@ RadListView {
|
|||
.text-btn {
|
||||
font-size: 14;
|
||||
horizontal-alignment: left;
|
||||
padding: 16;
|
||||
padding: 12;
|
||||
margin: 8 0 0 0;
|
||||
min-width: 0;
|
||||
}
|
||||
.closeBtn {
|
||||
MDButton.closeBtn {
|
||||
padding: 4;
|
||||
margin-top: 16;
|
||||
min-width: 0;
|
||||
vertical-alignment: top;
|
||||
}
|
||||
// -----------------------------
|
||||
// DIALOGS
|
||||
.dialogContainer {
|
||||
width: 100%;
|
||||
color: $grayD4;
|
||||
background: $grayL4;
|
||||
color: $gray9;
|
||||
background: $gray1;
|
||||
font-size: 16;
|
||||
&.dark {
|
||||
color: $grayL4;
|
||||
background: $grayD4;
|
||||
color: $gray1;
|
||||
background: $gray9;
|
||||
}
|
||||
.dialogTitle {
|
||||
padding: 24 24 12;
|
||||
padding: 24 24 16;
|
||||
font-size: 20;
|
||||
}
|
||||
.dialogInput {
|
||||
padding: 0 24 16;
|
||||
}
|
||||
.dialogDescription {
|
||||
line-height: 4;
|
||||
padding: 0 24 16;
|
||||
line-height: 6;
|
||||
padding: 0 24 8;
|
||||
}
|
||||
.actionItem {
|
||||
padding: 8 24;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
padding: 16 24;
|
||||
margin: 0;
|
||||
}
|
||||
.actionsContainer {
|
||||
padding: 24;
|
||||
padding: 8;
|
||||
}
|
||||
.action {
|
||||
font-size: 12;
|
||||
padding: 12;
|
||||
min-width: 0;
|
||||
color: #ff7043;
|
||||
}
|
||||
MDButton.actionIcon {
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-width: 0;
|
||||
padding: 16 24;
|
||||
border-radius: 4;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
margin: 0 16 16;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
ActivityIndicator {
|
||||
MDActivityIndicator {
|
||||
width: 24;
|
||||
height: 24;
|
||||
margin: 16;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<template>
|
||||
<Page @loaded="initializePage">
|
||||
<Page @loaded="onPageLoad">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<GridLayout rows="*" columns="auto, *">
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
class="bx"
|
||||
:text="icon.menu"
|
||||
automationText="Back"
|
||||
|
@ -21,67 +22,73 @@
|
|||
>
|
||||
<Image src="res://logo_light" class="appIcon" stretch="aspectFit" />
|
||||
</StackLayout>
|
||||
<StackLayout orientation="horizontal" class="option">
|
||||
<Label class="bx" :text="icon.info" />
|
||||
<StackLayout>
|
||||
<StackLayout class="m-8"></StackLayout>
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<Label col="0" class="bx" :text="icon.info" />
|
||||
<StackLayout col="1">
|
||||
<Label text="Version" />
|
||||
<Label :text="getVersion" class="option-info" textWrap="true" />
|
||||
<Label :text="getVersion" class="info" textWrap="true" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="option"
|
||||
@tap="openURL($event, 'https://github.com/vishnuraghavb/enrecipes')"
|
||||
>
|
||||
<Label class="bx" :text="icon.link" />
|
||||
<Label text="View project on GitHub" />
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="option"
|
||||
@tap="openURL($event, 'https://t.me/enrecipes')"
|
||||
>
|
||||
<Label class="bx" :text="icon.telegram" />
|
||||
<Label text="Join the Telegram group" />
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<MDRipple
|
||||
colSpan="2"
|
||||
@tap="openURL('https://github.com/vishnuraghavb/enrecipes')"
|
||||
/>
|
||||
<Label col="0" class="bx" :text="icon.link" />
|
||||
<Label
|
||||
verticalAlignment="center"
|
||||
col="1"
|
||||
text="View project on GitHub"
|
||||
/>
|
||||
</GridLayout>
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<MDRipple colSpan="2" @tap="openURL('https://t.me/enrecipes')" />
|
||||
<Label col="0" class="bx" :text="icon.telegram" />
|
||||
<Label
|
||||
verticalAlignment="center"
|
||||
col="1"
|
||||
text="Join the Telegram group"
|
||||
/>
|
||||
</GridLayout>
|
||||
|
||||
<StackLayout class="hr m-10"></StackLayout>
|
||||
|
||||
<Label text="Author" class="group-header" />
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="option"
|
||||
@tap="openURL($event, 'https://www.vishnuraghav.com')"
|
||||
>
|
||||
<Label class="bx" :text="icon.user" />
|
||||
<Label text="Vishnu Raghav" />
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="option"
|
||||
@tap="openURL($event, 'https://github.com/vishnuraghavb')"
|
||||
>
|
||||
<Label class="bx" :text="icon.link" />
|
||||
<Label text="Follow on GitHub" />
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="option"
|
||||
@tap="openURL($event, 'https://mastodon.social/@vishnuraghavb')"
|
||||
>
|
||||
<Label class="bx" :text="icon.link" />
|
||||
<Label text="Follow on Mastodon" />
|
||||
</StackLayout>
|
||||
<Label text="Author" class="group-header orkm" />
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<MDRipple
|
||||
colSpan="2"
|
||||
@tap="openURL('https://www.vishnuraghav.com')"
|
||||
/>
|
||||
<Label col="0" class="bx" :text="icon.user" />
|
||||
<Label verticalAlignment="center" col="1" text="Vishnu Raghav" />
|
||||
</GridLayout>
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<MDRipple
|
||||
colSpan="2"
|
||||
@tap="openURL('https://github.com/vishnuraghavb')"
|
||||
/>
|
||||
<Label col="0" class="bx" :text="icon.link" />
|
||||
<Label verticalAlignment="center" col="1" text="Follow on GitHub" />
|
||||
</GridLayout>
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<MDRipple
|
||||
colSpan="2"
|
||||
@tap="openURL('https://mastodon.social/@vishnuraghavb')"
|
||||
/>
|
||||
<Label col="0" class="bx" :text="icon.link" />
|
||||
<Label verticalAlignment="center" col="1" text="Follow on Mastodon" />
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Utils, Application } from "@nativescript/core"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
import { Application, Utils } from "@nativescript/core"
|
||||
import { mapActions, mapState } from "vuex"
|
||||
export default {
|
||||
props: ["highlight", "showDrawer", "title"],
|
||||
props: ["showDrawer", "title"],
|
||||
computed: {
|
||||
...mapState(["icon", "currentComponent"]),
|
||||
getVersion() {
|
||||
|
@ -98,16 +105,16 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
...mapActions(["setCurrentComponentAction"]),
|
||||
initializePage() {
|
||||
onPageLoad() {
|
||||
this.setCurrentComponentAction("About")
|
||||
},
|
||||
// HELPERS
|
||||
onScroll(args) {
|
||||
args.scrollY
|
||||
? (this.viewIsScrolled = true)
|
||||
: (this.viewIsScrolled = false)
|
||||
},
|
||||
openURL(args, url) {
|
||||
this.highlight(args)
|
||||
openURL(url) {
|
||||
Utils.openUrl(url)
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<Page
|
||||
@loaded="initialize"
|
||||
@loaded="onPageLoad"
|
||||
actionBarHidden="true"
|
||||
:androidStatusBarBackground="themeName == 'Light' ? '#fafafa' : '#212121'"
|
||||
:androidStatusBarBackground="appTheme == 'Light' ? '#f1f3f5' : '#212529'"
|
||||
>
|
||||
<RadSideDrawer
|
||||
ref="drawer"
|
||||
|
@ -19,12 +19,16 @@
|
|||
columns="auto, 24, *"
|
||||
v-for="(item, index) in topmenu"
|
||||
:key="index"
|
||||
@tap="navigateTo(item.component, false, false)"
|
||||
class="sd-item orkm"
|
||||
:class="{
|
||||
'selected-sd-item': currentComponent === item.component,
|
||||
}"
|
||||
>
|
||||
<MDRipple
|
||||
row="0"
|
||||
colSpan="3"
|
||||
@tap="navigateTo(item.component, false, false)"
|
||||
/>
|
||||
<Label col="0" row="0" class="bx" :text="icon[item.icon]" />
|
||||
<Label col="2" row="0" :text="item.title" />
|
||||
</GridLayout>
|
||||
|
@ -35,17 +39,17 @@
|
|||
columns="*, auto"
|
||||
v-if="categoriesWithRecipes.length"
|
||||
>
|
||||
<Label col="0" text="Categories" />
|
||||
<Label
|
||||
<Label verticalAlignment="center" col="0" text="Categories" />
|
||||
<MDButton
|
||||
variant="text"
|
||||
@tap="toggleCatEdit"
|
||||
col="2"
|
||||
:text="catEditMode ? 'DONE' : 'RENAME'"
|
||||
:text="editCategory ? 'DONE' : 'RENAME'"
|
||||
/>
|
||||
</GridLayout>
|
||||
<ScrollView height="100%">
|
||||
<StackLayout>
|
||||
<GridLayout
|
||||
@tap="navigateTo(item, false, true)"
|
||||
v-for="(item, index) in categoriesWithRecipes"
|
||||
:key="index"
|
||||
class="sd-item orkm"
|
||||
|
@ -54,6 +58,11 @@
|
|||
}"
|
||||
columns="auto, *, auto"
|
||||
>
|
||||
<MDRipple
|
||||
row="0"
|
||||
colSpan="3"
|
||||
@tap="navigateTo(item, false, true)"
|
||||
/>
|
||||
<Label
|
||||
col="0"
|
||||
class="bx"
|
||||
|
@ -61,8 +70,9 @@
|
|||
margin="0 24 0 0"
|
||||
/>
|
||||
<Label col="1" :text="item" />
|
||||
<Label
|
||||
v-if="catEditMode"
|
||||
<MDButton
|
||||
variant="text"
|
||||
v-if="editCategory"
|
||||
@tap="renameCategory(item)"
|
||||
col="2"
|
||||
class="bx"
|
||||
|
@ -74,30 +84,25 @@
|
|||
</StackLayout>
|
||||
<StackLayout row="1">
|
||||
<StackLayout class="hr m-10"></StackLayout>
|
||||
<!-- <StackLayout
|
||||
orientation="horizontal"
|
||||
class="sd-item orkm"
|
||||
@tap="donate"
|
||||
>
|
||||
<Label class="bx" :text="icon.donate" margin="0 24 0 0" />
|
||||
<Label text="Donate" />
|
||||
</StackLayout> -->
|
||||
<StackLayout
|
||||
@tap="navigateTo(item.component, true, false)"
|
||||
v-for="(item, index) in bottommenu"
|
||||
:key="index"
|
||||
orientation="horizontal"
|
||||
<GridLayout
|
||||
class="sd-item orkm"
|
||||
:class="{
|
||||
'selected-sd-item': currentComponent == item.title,
|
||||
}"
|
||||
v-for="(item, index) in bottommenu"
|
||||
:key="index"
|
||||
rows="48"
|
||||
columns="auto, 24, *"
|
||||
>
|
||||
<Label class="bx" :text="icon[item.icon]" margin="0 24 0 0" />
|
||||
<Label :text="item.title" />
|
||||
</StackLayout>
|
||||
<MDRipple
|
||||
colSpan="3"
|
||||
@tap="navigateTo(item.component, true, false)"
|
||||
/>
|
||||
<Label class="bx" col="0" :text="icon[item.icon]" />
|
||||
<Label col="2" :text="item.title" />
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
|
||||
<GridLayout ~mainContent rows="*" columns="*">
|
||||
<Frame row="0" col="0" ref="mainFrame" id="main-frame">
|
||||
<!-- Home -->
|
||||
|
@ -119,29 +124,28 @@
|
|||
|
||||
<script>
|
||||
import {
|
||||
Utils,
|
||||
ApplicationSettings,
|
||||
AndroidApplication,
|
||||
ApplicationEventData,
|
||||
Application,
|
||||
Color,
|
||||
Utils,
|
||||
} from "@nativescript/core"
|
||||
|
||||
import Theme from "@nativescript/theme"
|
||||
import * as Toast from "nativescript-toast"
|
||||
import * as application from "tns-core-modules/application"
|
||||
import { mapActions, mapState } from "vuex"
|
||||
|
||||
import EnRecipes from "./EnRecipes.vue"
|
||||
import Settings from "./Settings.vue"
|
||||
import About from "./About.vue"
|
||||
import PromptDialog from "./modal/PromptDialog.vue"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
|
||||
let page
|
||||
import PromptDialog from "./modal/PromptDialog.vue"
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EnRecipes,
|
||||
Settings,
|
||||
About,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
selectedCategory: null,
|
||||
|
@ -176,8 +180,8 @@ export default {
|
|||
icon: "info",
|
||||
},
|
||||
],
|
||||
catEditMode: false,
|
||||
themeName: "Light",
|
||||
editCategory: false,
|
||||
appTheme: "Light",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -203,16 +207,19 @@ export default {
|
|||
"initializeYieldUnits",
|
||||
"renameCategoryAction",
|
||||
]),
|
||||
initialize() {
|
||||
if (this.themeName === "Light") {
|
||||
onPageLoad() {
|
||||
if (this.appTheme === "Light") {
|
||||
const View = android.view.View
|
||||
const window = Application.android.startActivity.getWindow()
|
||||
const decorView = window.getDecorView()
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
|
||||
// window.setNavigationBarColor(new Color("#e0e0e0").android)
|
||||
}
|
||||
},
|
||||
|
||||
// HELPERS
|
||||
toggleCatEdit() {
|
||||
this.catEditMode = !this.catEditMode
|
||||
this.editCategory = !this.editCategory
|
||||
if (this.selectedCategory) this.setComponent("EnRecipes")
|
||||
this.filterFavorites = this.filterTrylater = false
|
||||
this.selectedCategory = null
|
||||
|
@ -226,99 +233,22 @@ export default {
|
|||
this.$showModal(PromptDialog, {
|
||||
props: {
|
||||
title: `Rename category`,
|
||||
hint: category,
|
||||
text: category,
|
||||
action: "RENAME",
|
||||
},
|
||||
}).then((newCategory) => {
|
||||
this.hijackGlobalBackEvent()
|
||||
if (newCategory.length) {
|
||||
this.renameCategoryAction({ current: category, updated: newCategory })
|
||||
this.catEditMode = false
|
||||
this.editCategory = false
|
||||
this.navigateTo(newCategory, false, true)
|
||||
}
|
||||
})
|
||||
},
|
||||
highlight(args) {
|
||||
let temp = args.object.className
|
||||
args.object.className = `${temp} option-highlight`
|
||||
setTimeout(() => (args.object.className = temp), 100)
|
||||
},
|
||||
// Navigation
|
||||
setSelectedCategory(e) {
|
||||
this.selectedCategory = e.item
|
||||
this.closeDrawer()
|
||||
},
|
||||
hijackGlobalBackEvent() {
|
||||
AndroidApplication.on(
|
||||
AndroidApplication.activityBackPressedEvent,
|
||||
this.globalBackEvent
|
||||
)
|
||||
},
|
||||
releaseGlobalBackEvent() {
|
||||
AndroidApplication.off(
|
||||
AndroidApplication.activityBackPressedEvent,
|
||||
this.globalBackEvent
|
||||
)
|
||||
},
|
||||
globalBackEvent(args) {
|
||||
function preventDefault() {
|
||||
args.cancel = true
|
||||
}
|
||||
let vm = this
|
||||
function isFiltered() {
|
||||
vm.filterFavorites
|
||||
? vm.setComponent("Favorites")
|
||||
: vm.filterTrylater
|
||||
? vm.setComponent("Try later")
|
||||
: vm.selectedCategory
|
||||
? vm.setComponent(vm.selectedCategory)
|
||||
: vm.setComponent("EnRecipes")
|
||||
}
|
||||
if (this.$refs.drawer && this.$refs.drawer.nativeView.getIsOpen()) {
|
||||
preventDefault()
|
||||
this.closeDrawer()
|
||||
this.catEditMode = false
|
||||
} else if (
|
||||
["Favorites", "Try later", this.selectedCategory].includes(
|
||||
this.currentComponent
|
||||
)
|
||||
) {
|
||||
preventDefault()
|
||||
this.setComponent("EnRecipes")
|
||||
this.filterFavorites = this.filterTrylater = false
|
||||
this.selectedCategory = null
|
||||
this.$refs.enrecipes.updateFilter()
|
||||
this.releaseGlobalBackEvent()
|
||||
}
|
||||
},
|
||||
navigateTo(to, isTrueComponent, isCategory) {
|
||||
if (isTrueComponent) {
|
||||
this.$navigateTo(to, {
|
||||
frame: "main-frame",
|
||||
props: {
|
||||
highlight: this.highlight,
|
||||
showDrawer: this.showDrawer,
|
||||
restartApp: this.restartApp,
|
||||
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
|
||||
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
|
||||
openAppSettingsPage: this.openAppSettingsPage,
|
||||
},
|
||||
backstackVisible: false,
|
||||
})
|
||||
this.closeDrawer()
|
||||
} else if (!this.catEditMode || !isCategory) {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.hijackGlobalBackEvent()
|
||||
this.setComponent(to)
|
||||
this.$navigateBack({ frame: "main-frame", backstackVisible: false })
|
||||
this.filterFavorites = to === "Favorites" ? true : false
|
||||
this.filterTrylater = to === "Try later" ? true : false
|
||||
this.selectedCategory = isCategory ? to : null
|
||||
this.$refs.enrecipes.updateFilter()
|
||||
this.closeDrawer()
|
||||
}
|
||||
this.catEditMode = false
|
||||
},
|
||||
restartApp() {
|
||||
// Code from nativescript-master-technology
|
||||
const mStartActivity = new android.content.Intent(
|
||||
|
@ -360,19 +290,91 @@ export default {
|
|||
closeDrawer() {
|
||||
this.$refs.drawer.nativeView.closeDrawer()
|
||||
},
|
||||
donate(args) {
|
||||
this.highlight(args)
|
||||
|
||||
// NAVIGATION HANDLERS
|
||||
hijackGlobalBackEvent() {
|
||||
AndroidApplication.on(
|
||||
AndroidApplication.activityBackPressedEvent,
|
||||
this.globalBackEvent
|
||||
)
|
||||
},
|
||||
releaseGlobalBackEvent() {
|
||||
AndroidApplication.off(
|
||||
AndroidApplication.activityBackPressedEvent,
|
||||
this.globalBackEvent
|
||||
)
|
||||
},
|
||||
globalBackEvent(args) {
|
||||
function preventDefault() {
|
||||
args.cancel = true
|
||||
}
|
||||
let vm = this
|
||||
function isFiltered() {
|
||||
vm.filterFavorites
|
||||
? vm.setComponent("Favorites")
|
||||
: vm.filterTrylater
|
||||
? vm.setComponent("Try later")
|
||||
: vm.selectedCategory
|
||||
? vm.setComponent(vm.selectedCategory)
|
||||
: vm.setComponent("EnRecipes")
|
||||
}
|
||||
if (this.$refs.drawer && this.$refs.drawer.nativeView.getIsOpen()) {
|
||||
preventDefault()
|
||||
this.closeDrawer()
|
||||
this.editCategory = false
|
||||
} else if (
|
||||
["Favorites", "Try later", this.selectedCategory].includes(
|
||||
this.currentComponent
|
||||
)
|
||||
) {
|
||||
preventDefault()
|
||||
this.setComponent("EnRecipes")
|
||||
this.filterFavorites = this.filterTrylater = false
|
||||
this.selectedCategory = null
|
||||
this.$refs.enrecipes.updateFilter()
|
||||
this.releaseGlobalBackEvent()
|
||||
}
|
||||
},
|
||||
navigateTo(to, isTrueComponent, isCategory) {
|
||||
if (isTrueComponent) {
|
||||
this.$navigateTo(to, {
|
||||
frame: "main-frame",
|
||||
props: {
|
||||
showDrawer: this.showDrawer,
|
||||
restartApp: this.restartApp,
|
||||
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
|
||||
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
|
||||
openAppSettingsPage: this.openAppSettingsPage,
|
||||
},
|
||||
backstackVisible: false,
|
||||
})
|
||||
this.closeDrawer()
|
||||
} else if (!this.editCategory || !isCategory) {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.hijackGlobalBackEvent()
|
||||
this.setComponent(to)
|
||||
this.$navigateBack({ frame: "main-frame", backstackVisible: false })
|
||||
this.filterFavorites = to === "Favorites" ? true : false
|
||||
this.filterTrylater = to === "Try later" ? true : false
|
||||
this.selectedCategory = isCategory ? to : null
|
||||
this.$refs.enrecipes.updateFilter()
|
||||
this.closeDrawer()
|
||||
}
|
||||
this.editCategory = false
|
||||
},
|
||||
|
||||
donate() {
|
||||
Utils.openUrl("https://www.vishnuraghav.com/donate/")
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.themeName = ApplicationSettings.getString("appTheme", "Light")
|
||||
this.appTheme = ApplicationSettings.getString("appTheme", "Light")
|
||||
setTimeout((e) => {
|
||||
Theme.setMode(Theme[this.themeName])
|
||||
Theme.setMode(Theme[this.appTheme])
|
||||
}, 50)
|
||||
if (!this.recipes.length) this.initializeRecipes()
|
||||
if (!this.categories.length) this.initializeCategories()
|
||||
if (!this.yieldUnits.length) this.initializeYieldUnits()
|
||||
this.initializeRecipes()
|
||||
this.initializeCategories()
|
||||
this.initializeYieldUnits()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<template>
|
||||
<Page @loaded="initialize" @unloaded="releaseBackEvent">
|
||||
<Page @loaded="onPageLoad" @unloaded="releaseBackEvent">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<GridLayout rows="*" columns="auto, *, auto">
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
class="bx"
|
||||
:text="icon.back"
|
||||
automationText="Back"
|
||||
|
@ -10,14 +11,15 @@
|
|||
@tap="navigateBack"
|
||||
/>
|
||||
<Label class="title orkm" :text="title" col="1" />
|
||||
<Label
|
||||
v-if="hasEnoughDetails && !imageLoading"
|
||||
<MDButton
|
||||
variant="text"
|
||||
v-if="hasChanges && !imageLoading"
|
||||
class="bx"
|
||||
:text="icon.save"
|
||||
col="2"
|
||||
@tap="saveOperation"
|
||||
/>
|
||||
<ActivityIndicator col="2" v-if="imageLoading" :busy="imageLoading" />
|
||||
<MDActivityIndicator col="2" v-if="imageLoading" :busy="imageLoading" />
|
||||
</GridLayout>
|
||||
</ActionBar>
|
||||
<GridLayout rows="*" columns="*">
|
||||
|
@ -51,18 +53,16 @@
|
|||
:text="icon.image"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout width="100%" :top="screenWidth - 42">
|
||||
<transition :name="recipeContent.imageSrc ? 'null' : 'bounce'">
|
||||
<Label
|
||||
v-if="showFab"
|
||||
horizontalAlignment="right"
|
||||
@tap="imageHandler"
|
||||
class="bx fab-button"
|
||||
:text="icon.camera"
|
||||
androidElevation="6"
|
||||
/>
|
||||
</transition>
|
||||
</StackLayout>
|
||||
<transition :name="recipeContent.imageSrc ? 'null' : 'bounce'">
|
||||
<MDFloatingActionButton
|
||||
v-if="showFab"
|
||||
:top="screenWidth - 44"
|
||||
:left="screenWidth - 88"
|
||||
class="bx"
|
||||
src="res://camera"
|
||||
@tap="imageHandler"
|
||||
/>
|
||||
</transition>
|
||||
</AbsoluteLayout>
|
||||
|
||||
<StackLayout margin="0 16">
|
||||
|
@ -71,6 +71,7 @@
|
|||
hint="My Healthy Recipe"
|
||||
v-model="recipeContent.title"
|
||||
autocapitalizationType="words"
|
||||
autocorrect="true"
|
||||
/>
|
||||
<Label top="0" class="fieldLabel" text="Title" />
|
||||
</AbsoluteLayout>
|
||||
|
@ -103,7 +104,7 @@
|
|||
<GridLayout columns="*, 8, *">
|
||||
<AbsoluteLayout class="inputField" col="0">
|
||||
<TextField
|
||||
:text="formattedTimeRequired"
|
||||
:text="timeRequired"
|
||||
editable="false"
|
||||
@tap="setTimeRequired"
|
||||
/>
|
||||
|
@ -127,30 +128,33 @@
|
|||
v-model="recipeContent.ingredients[index].item"
|
||||
:hint="`Item ${index + 1}`"
|
||||
autocapitalizationType="words"
|
||||
autocorrect="true"
|
||||
/>
|
||||
<TextField
|
||||
width="72"
|
||||
width="68"
|
||||
col="2"
|
||||
v-model="recipeContent.ingredients[index].quantity"
|
||||
hint="1.000"
|
||||
hint="1.00"
|
||||
keyboardType="number"
|
||||
/>
|
||||
<TextField
|
||||
width="64"
|
||||
width="68"
|
||||
col="4"
|
||||
v-model="recipeContent.ingredients[index].unit"
|
||||
hint="Unit"
|
||||
editable="false"
|
||||
@tap="showUnits($event)"
|
||||
/>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
col="6"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeIngredient(index)"
|
||||
/>
|
||||
</GridLayout>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
class="text-btn orkm"
|
||||
text="+ ADD INGREDIENT"
|
||||
@tap="addIngredient()"
|
||||
|
@ -172,15 +176,18 @@
|
|||
:hint="`Step ${index + 1}`"
|
||||
v-model="recipeContent.instructions[index]"
|
||||
editable="true"
|
||||
autocorrect="true"
|
||||
/>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
col="2"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeInstruction(index)"
|
||||
/>
|
||||
</GridLayout>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
class="text-btn orkm"
|
||||
text="+ ADD STEP"
|
||||
@tap="addInstruction()"
|
||||
|
@ -201,15 +208,22 @@
|
|||
v-model="recipeContent.notes[index]"
|
||||
:hint="`Note ${index + 1}`"
|
||||
editable="true"
|
||||
autocorrect="true"
|
||||
/>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
col="2"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeNote(index)"
|
||||
/>
|
||||
</GridLayout>
|
||||
<Label class="text-btn orkm" text="+ ADD NOTE" @tap="addNote()" />
|
||||
<MDButton
|
||||
variant="text"
|
||||
class="text-btn orkm"
|
||||
text="+ ADD NOTE"
|
||||
@tap="addNote()"
|
||||
/>
|
||||
<StackLayout class="hr" margin="24 16"></StackLayout>
|
||||
</StackLayout>
|
||||
|
||||
|
@ -226,15 +240,18 @@
|
|||
v-model="recipeContent.references[index]"
|
||||
hint="Text or Website/Video URL"
|
||||
editable="true"
|
||||
autocorrect="true"
|
||||
/>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
col="2"
|
||||
class="bx closeBtn"
|
||||
:text="icon.close"
|
||||
@tap="removeReference(index)"
|
||||
/>
|
||||
</GridLayout>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
class="text-btn orkm"
|
||||
text="+ ADD REFERENCE"
|
||||
@tap="addReference()"
|
||||
|
@ -248,31 +265,30 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { WorkerService } from "../worker.service"
|
||||
const workerService = new WorkerService()
|
||||
|
||||
// import { WorkerService } from "../worker.service"
|
||||
// const workerService = new WorkerService()
|
||||
import {
|
||||
Screen,
|
||||
AndroidApplication,
|
||||
ImageSource,
|
||||
path,
|
||||
getFileAccess,
|
||||
knownFolders,
|
||||
Utils,
|
||||
File,
|
||||
Application,
|
||||
ApplicationSettings,
|
||||
File,
|
||||
getFileAccess,
|
||||
ImageSource,
|
||||
knownFolders,
|
||||
path,
|
||||
Screen,
|
||||
Utils,
|
||||
} from "@nativescript/core"
|
||||
|
||||
import { Mediafilepicker } from "nativescript-mediafilepicker"
|
||||
|
||||
import * as Permissions from "@nativescript-community/perms"
|
||||
import * as Toast from "nativescript-toast"
|
||||
import * as ImagePicker from "@nativescript/imagepicker"
|
||||
import { ImageCropper } from "nativescript-imagecropper"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
|
||||
import ActionDialog from "./modal/ActionDialog.vue"
|
||||
import ConfirmDialog from "./modal/ConfirmDialog.vue"
|
||||
import PromptDialog from "./modal/PromptDialog.vue"
|
||||
import ListPicker from "./modal/ListPicker.vue"
|
||||
import * as Permissions from "@nativescript-community/perms"
|
||||
import * as Toast from "nativescript-toast"
|
||||
|
||||
export default {
|
||||
props: [
|
||||
|
@ -310,6 +326,7 @@ export default {
|
|||
newRecipeID: null,
|
||||
showFab: false,
|
||||
imageLoading: false,
|
||||
cacheImagePath: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -324,13 +341,16 @@ export default {
|
|||
screenWidth() {
|
||||
return Screen.mainScreen.widthDIPs
|
||||
},
|
||||
hasEnoughDetails() {
|
||||
appTheme() {
|
||||
return Application.systemAppearance()
|
||||
},
|
||||
hasChanges() {
|
||||
return (
|
||||
JSON.stringify(this.recipeContent) !==
|
||||
JSON.stringify(this.tempRecipeContent)
|
||||
)
|
||||
},
|
||||
formattedTimeRequired() {
|
||||
timeRequired() {
|
||||
let t = this.recipeContent.timeRequired.split(":")
|
||||
let h = parseInt(t[0])
|
||||
let m = parseInt(t[1])
|
||||
|
@ -345,7 +365,7 @@ export default {
|
|||
"addCategoryAction",
|
||||
"addYieldUnitAction",
|
||||
]),
|
||||
initialize() {
|
||||
onPageLoad() {
|
||||
this.showFab = true
|
||||
},
|
||||
|
||||
|
@ -394,11 +414,11 @@ export default {
|
|||
props: {
|
||||
title: "Category",
|
||||
list: [...this.categories],
|
||||
height: "408",
|
||||
action: "CREATE NEW",
|
||||
// height: "420",
|
||||
action: "ADD NEW",
|
||||
},
|
||||
}).then((action) => {
|
||||
if (action == "CREATE NEW") {
|
||||
if (action == "ADD NEW") {
|
||||
this.$showModal(PromptDialog, {
|
||||
props: {
|
||||
title: "New category",
|
||||
|
@ -425,11 +445,11 @@ export default {
|
|||
props: {
|
||||
title: "Yield measured in",
|
||||
list: [...this.yieldUnits],
|
||||
height: "408",
|
||||
action: "CREATE NEW",
|
||||
// height: "420",
|
||||
action: "ADD NEW",
|
||||
},
|
||||
}).then((action) => {
|
||||
if (action == "CREATE NEW") {
|
||||
if (action == "ADD NEW") {
|
||||
this.$showModal(PromptDialog, {
|
||||
props: {
|
||||
title: "New yield unit",
|
||||
|
@ -456,7 +476,7 @@ export default {
|
|||
props: {
|
||||
title: "Unit",
|
||||
list: [...this.units],
|
||||
height: "408",
|
||||
// height: "420",
|
||||
},
|
||||
}).then((action) => {
|
||||
this.hijackBackEvent()
|
||||
|
@ -466,21 +486,19 @@ export default {
|
|||
|
||||
// NAVIGATION HANDLERS
|
||||
navigateBack() {
|
||||
if (this.hasEnoughDetails) {
|
||||
if (this.hasChanges) {
|
||||
this.blockModal = true
|
||||
this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: "Unsaved changes",
|
||||
description:
|
||||
"Do you want to save the changes you made in this recipe?",
|
||||
"Are you sure you want to discard unsaved changes to this recipe?",
|
||||
cancelButtonText: "DISCARD",
|
||||
okButtonText: "SAVE",
|
||||
okButtonText: "KEEP EDITING",
|
||||
},
|
||||
}).then((action) => {
|
||||
this.blockModal = false
|
||||
if (action) {
|
||||
this.saveOperation()
|
||||
} else if (action != null) {
|
||||
if (action != null && !action) {
|
||||
this.$navigateBack()
|
||||
this.releaseBackEvent()
|
||||
}
|
||||
|
@ -503,7 +521,7 @@ export default {
|
|||
)
|
||||
},
|
||||
backEvent(args) {
|
||||
if (this.hasEnoughDetails && !this.blockModal) {
|
||||
if (this.hasChanges && !this.blockModal) {
|
||||
args.cancel = true
|
||||
this.navigateBack()
|
||||
}
|
||||
|
@ -522,28 +540,22 @@ export default {
|
|||
}).then((action) => {
|
||||
this.blockModal = false
|
||||
if (action) {
|
||||
this.permissionCheck(
|
||||
this.imagePickerPermissionConfirmation,
|
||||
this.imagePicker
|
||||
)
|
||||
this.permissionCheck(this.permissionConfirmation, this.imagePicker)
|
||||
} else if (action != null) {
|
||||
this.recipeContent.imageSrc = null
|
||||
this.releaseBackEvent()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.permissionCheck(
|
||||
this.imagePickerPermissionConfirmation,
|
||||
this.imagePicker
|
||||
)
|
||||
this.permissionCheck(this.permissionConfirmation, this.imagePicker)
|
||||
}
|
||||
},
|
||||
imagePickerPermissionConfirmation() {
|
||||
permissionConfirmation() {
|
||||
return this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: "Grant permission",
|
||||
description:
|
||||
"EnRecipes requires storage and camera permission in order to set recipe photo.",
|
||||
"EnRecipes requires storage permission in order to set recipe photo.",
|
||||
cancelButtonText: "NOT NOW",
|
||||
okButtonText: "CONTINUE",
|
||||
},
|
||||
|
@ -553,52 +565,82 @@ export default {
|
|||
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) {
|
||||
confirmation().then((e) => {
|
||||
if (e) {
|
||||
Permissions.request("camera").then((res) => {
|
||||
let status = res[Object.keys(res)[0]]
|
||||
if (status === "authorized") action()
|
||||
if (status === "never_ask_again")
|
||||
ApplicationSettings.setBoolean("storagePermissionAsked", true)
|
||||
if (status === "denied")
|
||||
Toast.makeText("Permission denied").show()
|
||||
Permissions.request("photo").then((status) => {
|
||||
switch (status[0]) {
|
||||
case "authorized":
|
||||
action()
|
||||
break
|
||||
case "never_ask_again":
|
||||
ApplicationSettings.setBoolean("storagePermissionAsked", true)
|
||||
break
|
||||
case "denied":
|
||||
Toast.makeText("Permission denied").show()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Permissions.check("camera").then((res) => {
|
||||
if (res[0] !== "authorized") {
|
||||
confirmation().then((e) => {
|
||||
e && this.openAppSettingsPage()
|
||||
})
|
||||
} else {
|
||||
Permissions.request("storage").then((res) => {
|
||||
let status = res[Object.keys(res)[0]]
|
||||
if (status !== "authorized") {
|
||||
confirmation().then((e) => {
|
||||
e && this.openAppSettingsPage()
|
||||
})
|
||||
} else action()
|
||||
})
|
||||
}
|
||||
Permissions.check("photo").then((res) => {
|
||||
res[0] !== "authorized"
|
||||
? confirmation().then((e) => e && this.openAppSettingsPage())
|
||||
: action()
|
||||
})
|
||||
}
|
||||
},
|
||||
imagePicker() {
|
||||
const vm = this
|
||||
const mediafilepicker = new Mediafilepicker()
|
||||
mediafilepicker.openImagePicker({
|
||||
android: {
|
||||
isCaptureMood: false, // if true then camera will open directly.
|
||||
isNeedCamera: true,
|
||||
maxNumberFiles: 1,
|
||||
isNeedFolderList: true,
|
||||
},
|
||||
})
|
||||
mediafilepicker.on("getFiles", (image) => {
|
||||
ApplicationSettings.setBoolean("storagePermissionAsked", true)
|
||||
vm.recipeContent.imageSrc = image.object.get("results")[0].file
|
||||
ApplicationSettings.setBoolean("storagePermissionAsked", true)
|
||||
this.cacheImagePath = path.join(
|
||||
knownFolders.temp().path,
|
||||
`${this.getRandomID()}.jpg`
|
||||
)
|
||||
let screenWidth = Math.round(this.screenWidth * 2)
|
||||
ImagePicker.create({
|
||||
mode: "single",
|
||||
mediaType: ImagePicker.ImagePickerMediaType.Image,
|
||||
})
|
||||
.present()
|
||||
.then((selection) => {
|
||||
let imgPath = selection[0]._android
|
||||
ImageSource.fromFile(imgPath).then((image) => {
|
||||
ImageCropper.prototype
|
||||
.show(
|
||||
image,
|
||||
{
|
||||
width: screenWidth,
|
||||
height: screenWidth,
|
||||
},
|
||||
{
|
||||
hideBottomControls: true,
|
||||
toolbarTitle: "Crop photo",
|
||||
statusBarColor: "#ff5200",
|
||||
toolbarTextColor:
|
||||
this.appTheme == "light" ? "#212529" : "#f1f3f5",
|
||||
toolbarColor:
|
||||
this.appTheme == "light" ? "#f1f3f5" : "#212529",
|
||||
cropFrameColor: "#ff5200",
|
||||
}
|
||||
)
|
||||
.then((cropped) => {
|
||||
cropped.image.saveToFile(this.cacheImagePath, "jpg", 75)
|
||||
this.recipeContent.imageSrc = this.cacheImagePath
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// INPUT FIELD HANDLERS
|
||||
fieldDeletionConfirm(item) {
|
||||
return this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: `Delete ${item}?`,
|
||||
cancelButtonText: "CANCEL",
|
||||
okButtonText: "DELETE",
|
||||
},
|
||||
})
|
||||
},
|
||||
addIngredient() {
|
||||
this.recipeContent.ingredients.push({
|
||||
item: "",
|
||||
|
@ -607,30 +649,55 @@ export default {
|
|||
})
|
||||
},
|
||||
removeIngredient(index) {
|
||||
this.recipeContent.ingredients.splice(index, 1)
|
||||
if (this.recipeContent.ingredients[index].item.length) {
|
||||
this.fieldDeletionConfirm("ingredient").then((res) => {
|
||||
if (res) {
|
||||
this.recipeContent.ingredients.splice(index, 1)
|
||||
}
|
||||
})
|
||||
} else this.recipeContent.ingredients.splice(index, 1)
|
||||
},
|
||||
|
||||
addInstruction() {
|
||||
this.recipeContent.instructions.push("")
|
||||
},
|
||||
removeInstruction(index) {
|
||||
this.recipeContent.instructions.splice(index, 1)
|
||||
if (this.recipeContent.instructions[index].length) {
|
||||
this.fieldDeletionConfirm("instruction").then((res) => {
|
||||
if (res) {
|
||||
this.recipeContent.instructions.splice(index, 1)
|
||||
}
|
||||
})
|
||||
} else this.recipeContent.instructions.splice(index, 1)
|
||||
},
|
||||
|
||||
addNote() {
|
||||
this.recipeContent.notes.push("")
|
||||
},
|
||||
removeNote(index) {
|
||||
this.recipeContent.notes.splice(index, 1)
|
||||
if (this.recipeContent.notes[index].length) {
|
||||
this.fieldDeletionConfirm("note").then((res) => {
|
||||
if (res) {
|
||||
this.recipeContent.notes.splice(index, 1)
|
||||
}
|
||||
})
|
||||
} else this.recipeContent.notes.splice(index, 1)
|
||||
},
|
||||
|
||||
addReference() {
|
||||
this.recipeContent.references.push("")
|
||||
},
|
||||
removeReference(index) {
|
||||
this.recipeContent.references.splice(index, 1)
|
||||
if (this.recipeContent.references[index].length) {
|
||||
this.fieldDeletionConfirm("reference").then((res) => {
|
||||
if (res) {
|
||||
this.recipeContent.references.splice(index, 1)
|
||||
}
|
||||
})
|
||||
} else this.recipeContent.references.splice(index, 1)
|
||||
},
|
||||
|
||||
// SAVE OPERATION
|
||||
clearEmptyFields() {
|
||||
if (!this.recipeContent.title)
|
||||
this.recipeContent.title = "Untitled Recipe"
|
||||
|
@ -651,37 +718,50 @@ export default {
|
|||
this.imageLoading = true
|
||||
this.clearEmptyFields()
|
||||
this.recipeContent.lastModified = new Date()
|
||||
if (this.cacheImagePath) {
|
||||
let recipeImage = path.join(
|
||||
knownFolders
|
||||
.documents()
|
||||
.getFolder("EnRecipes")
|
||||
.getFolder("Images").path,
|
||||
`${this.getRandomID()}.jpg`
|
||||
)
|
||||
let binarySource = File.fromPath(this.cacheImagePath).readSync()
|
||||
File.fromPath(recipeImage).writeSync(binarySource)
|
||||
this.recipeContent.imageSrc = recipeImage
|
||||
knownFolders.temp().clear()
|
||||
}
|
||||
if (this.recipeContent.imageSrc) {
|
||||
if (this.tempRecipeContent.imageSrc) {
|
||||
if (this.tempRecipeContent.imageSrc !== this.recipeContent.imageSrc) {
|
||||
getFileAccess().deleteFile(this.tempRecipeContent.imageSrc)
|
||||
this.imageSaveOperation()
|
||||
} else this.saveRecipe()
|
||||
} else this.imageSaveOperation()
|
||||
if (
|
||||
this.tempRecipeContent.imageSrc &&
|
||||
this.tempRecipeContent.imageSrc !== this.recipeContent.imageSrc
|
||||
) {
|
||||
getFileAccess().deleteFile(this.tempRecipeContent.imageSrc)
|
||||
}
|
||||
} else if (this.tempRecipeContent.imageSrc) {
|
||||
getFileAccess().deleteFile(this.tempRecipeContent.imageSrc)
|
||||
this.saveRecipe()
|
||||
} else this.saveRecipe()
|
||||
},
|
||||
imageSaveOperation() {
|
||||
let imgSavedToPath = path.join(
|
||||
knownFolders
|
||||
.documents()
|
||||
.getFolder("EnRecipes")
|
||||
.getFolder("Images").path,
|
||||
`${this.getRandomID()}.jpg`
|
||||
)
|
||||
let workerService = new WorkerService()
|
||||
let ImageProcessor = workerService.initImageProcessor()
|
||||
ImageProcessor.postMessage({
|
||||
imgFile: this.recipeContent.imageSrc,
|
||||
imgSavedToPath,
|
||||
})
|
||||
ImageProcessor.onmessage = ({ data }) => {
|
||||
this.recipeContent.imageSrc = imgSavedToPath
|
||||
this.saveRecipe()
|
||||
}
|
||||
this.saveRecipe()
|
||||
},
|
||||
// imageSaveOperation() {
|
||||
// let imgSavedToPath = path.join(
|
||||
// knownFolders
|
||||
// .documents()
|
||||
// .getFolder("EnRecipes")
|
||||
// .getFolder("Images").path,
|
||||
// `${this.getRandomID()}.jpg`
|
||||
// )
|
||||
// let workerService = new WorkerService()
|
||||
// let ImageProcessor = workerService.initImageProcessor()
|
||||
// ImageProcessor.postMessage({
|
||||
// imgFile: this.recipeContent.imageSrc,
|
||||
// imgSavedToPath,
|
||||
// })
|
||||
// ImageProcessor.onmessage = ({ data }) => {
|
||||
// this.recipeContent.imageSrc = imgSavedToPath
|
||||
// this.saveRecipe()
|
||||
// }
|
||||
// },
|
||||
saveRecipe() {
|
||||
if (this.recipeID) {
|
||||
this.overwriteRecipeAction({
|
||||
|
@ -705,6 +785,7 @@ export default {
|
|||
this.setCurrentComponentAction("EditRecipe")
|
||||
}, 500)
|
||||
this.title = this.recipeID ? "Edit recipe" : "New recipe"
|
||||
|
||||
if (this.recipeID) {
|
||||
let recipe = this.recipes.filter((e) => e.id === this.recipeID)[0]
|
||||
Object.assign(this.recipeContent, JSON.parse(JSON.stringify(recipe)))
|
||||
|
@ -713,13 +794,14 @@ export default {
|
|||
JSON.parse(JSON.stringify(this.recipeContent))
|
||||
)
|
||||
} else {
|
||||
if (this.selectedCategory)
|
||||
this.recipeContent.category = this.selectedCategory
|
||||
if (this.filterFavorites) this.recipeContent.isFavorite = true
|
||||
Object.assign(
|
||||
this.tempRecipeContent,
|
||||
JSON.parse(JSON.stringify(this.recipeContent))
|
||||
)
|
||||
if (this.selectedCategory)
|
||||
this.recipeContent.category = this.selectedCategory
|
||||
if (this.filterFavorites) this.recipeContent.isFavorite = true
|
||||
|
||||
this.newRecipeID = this.getRandomID()
|
||||
}
|
||||
this.hijackBackEvent()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Page @loaded="initializePage">
|
||||
<Page @loaded="onPageLoad">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<!-- Search Actionbar -->
|
||||
<GridLayout
|
||||
|
@ -7,9 +7,10 @@
|
|||
columns="auto, *"
|
||||
verticalAlignment="center"
|
||||
>
|
||||
<Label
|
||||
<MDButton
|
||||
class="bx"
|
||||
:text="icon.back"
|
||||
variant="text"
|
||||
automationText="Back"
|
||||
col="0"
|
||||
@tap="closeSearch"
|
||||
|
@ -17,33 +18,35 @@
|
|||
<SearchBar
|
||||
col="1"
|
||||
hint="Search"
|
||||
textFieldHintColor="#bdbdbd"
|
||||
v-model="searchQuery"
|
||||
@textChange="updateFilter"
|
||||
@clear="updateFilter"
|
||||
@clear="clearSearch"
|
||||
/>
|
||||
</GridLayout>
|
||||
<!-- Home Actionbar -->
|
||||
<GridLayout v-else columns="auto, *, auto, auto">
|
||||
<Label
|
||||
<MDButton
|
||||
class="bx"
|
||||
col="0"
|
||||
variant="text"
|
||||
@tap="showDrawer"
|
||||
:text="icon.menu"
|
||||
automationText="Back"
|
||||
@tap="showDrawer"
|
||||
col="0"
|
||||
/>
|
||||
<Label class="title orkm" :text="currentComponent" col="1" />
|
||||
<Label
|
||||
<MDButton
|
||||
v-if="recipes.length"
|
||||
class="bx"
|
||||
:text="icon.search"
|
||||
variant="text"
|
||||
col="2"
|
||||
@tap="openSearch"
|
||||
/>
|
||||
<Label
|
||||
<MDButton
|
||||
v-if="recipes.length"
|
||||
class="bx"
|
||||
:text="icon.sort"
|
||||
variant="text"
|
||||
col="3"
|
||||
@tap="sortDialog"
|
||||
/>
|
||||
|
@ -58,10 +61,10 @@
|
|||
@itemSwipeProgressChanged="onSwiping"
|
||||
@itemSwipeProgressEnded="onSwipeEnded"
|
||||
@scrolled="onScroll"
|
||||
@itemTap="viewRecipe"
|
||||
:filteringFunction="filterFunction"
|
||||
:sortingFunction="sortFunction"
|
||||
>
|
||||
<!-- @itemTap="viewRecipe" -->
|
||||
<v-template>
|
||||
<GridLayout
|
||||
class="recipeItem"
|
||||
|
@ -69,6 +72,7 @@
|
|||
columns="112, *"
|
||||
androidElevation="1"
|
||||
>
|
||||
<MDRipple colSpan="2" @tap="viewRecipe(recipe)" />
|
||||
<GridLayout class="imageHolder card" rows="112" columns="112">
|
||||
<Image
|
||||
row="0"
|
||||
|
@ -114,31 +118,85 @@
|
|||
<StackLayout height="128"></StackLayout>
|
||||
</v-template>
|
||||
</RadListView>
|
||||
<GridLayout rows="96, auto, *" columns="*" class="emptyState">
|
||||
<GridLayout rows="*, auto, *, 88" columns="*" class="emptyStateContainer">
|
||||
<StackLayout
|
||||
col="0"
|
||||
row="1"
|
||||
class="noResult"
|
||||
v-if="!recipes.length && !filterFavorites && !filterTrylater"
|
||||
class="emptyState"
|
||||
v-if="
|
||||
!recipes.length &&
|
||||
!filterFavorites &&
|
||||
!filterTrylater &&
|
||||
!selectedCategory
|
||||
"
|
||||
@tap="addRecipe"
|
||||
>
|
||||
<Image class="logo" src="res://icon_gray" stretch="aspectFit" />
|
||||
<Label class="bx icon" :text="icon.plusCircle" />
|
||||
<Label
|
||||
class="title orkm"
|
||||
text="Start adding your recipes!"
|
||||
textWrap="true"
|
||||
/>
|
||||
<StackLayout orientation="horizontal" horizontalAlignment="center">
|
||||
<Label text="Use the " textWrap="true" />
|
||||
<Label text="Use the " />
|
||||
<Label class="bx" :text="icon.plus" />
|
||||
<Label text=" button to add a new recipe" textWrap="true" />
|
||||
<Label text=" button to add one" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
col="0"
|
||||
row="1"
|
||||
class="noResult"
|
||||
class="emptyState"
|
||||
v-if="!filteredRecipes.length && filterFavorites && !searchQuery"
|
||||
>
|
||||
<Label class="bx icon" :text="icon.heartOutline" textWrap="true" />
|
||||
<Label class="title orkm" text="No favorites yet" textWrap="true" />
|
||||
<Label
|
||||
text="Recipes you mark as favorite will be listed here"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
col="0"
|
||||
row="1"
|
||||
class="emptyState"
|
||||
v-if="!filteredRecipes.length && filterTrylater && !searchQuery"
|
||||
>
|
||||
<Label class="bx icon" :text="icon.trylaterOutline" textWrap="true" />
|
||||
<Label class="title orkm" text="All done!" textWrap="true" />
|
||||
<Label
|
||||
text="Recipes you mark as try later will be listed here"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
col="0"
|
||||
row="1"
|
||||
class="emptyState"
|
||||
v-if="
|
||||
!filteredRecipes.length &&
|
||||
!filterFavorites &&
|
||||
!filterTrylater &&
|
||||
selectedCategory
|
||||
"
|
||||
>
|
||||
<Label class="bx icon" :text="icon.labelOutline" textWrap="true" />
|
||||
<Label
|
||||
class="title orkm"
|
||||
text="Category looks empty"
|
||||
textWrap="true"
|
||||
/>
|
||||
<StackLayout orientation="horizontal" horizontalAlignment="center">
|
||||
<Label text="Use the " textWrap="true" />
|
||||
<Label class="bx" :text="icon.plus" />
|
||||
<Label text=" button to add a recipe" textWrap="true" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
col="0"
|
||||
row="0"
|
||||
class="emptyState noResult"
|
||||
v-if="!filteredRecipes.length && searchQuery"
|
||||
verticalAlignment="top"
|
||||
>
|
||||
<Label class="bx icon" :text="icon.search" textWrap="true" />
|
||||
<Label class="title orkm" text="No recipes found" textWrap="true" />
|
||||
|
@ -153,45 +211,15 @@
|
|||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
col="0"
|
||||
row="1"
|
||||
class="noResult"
|
||||
v-if="!filteredRecipes.length && filterFavorites && !searchQuery"
|
||||
>
|
||||
<Label class="bx icon" :text="icon.heartOutline" textWrap="true" />
|
||||
<Label class="title orkm" text="No favorites yet!" textWrap="true" />
|
||||
<Label
|
||||
text="Your favorited recipes will be listed here"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
col="0"
|
||||
row="1"
|
||||
class="noResult"
|
||||
v-if="!filteredRecipes.length && filterTrylater && !searchQuery"
|
||||
>
|
||||
<Label class="bx icon" :text="icon.trylaterOutline" textWrap="true" />
|
||||
<Label
|
||||
class="title orkm"
|
||||
text="Nothing to try next!"
|
||||
textWrap="true"
|
||||
/>
|
||||
<Label
|
||||
text="Recipes you wanted to try later will be listed here"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
<GridLayout id="btnFabContainer" rows="*,auto" columns="*,auto">
|
||||
<GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
|
||||
<transition name="bounce">
|
||||
<Label
|
||||
<MDFloatingActionButton
|
||||
v-if="showFAB"
|
||||
row="1"
|
||||
col="1"
|
||||
class="bx fab-button"
|
||||
:text="icon.plus"
|
||||
src="res://plus"
|
||||
@tap="addRecipe"
|
||||
/>
|
||||
</transition>
|
||||
|
@ -201,13 +229,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Utils, AndroidApplication } from "@nativescript/core"
|
||||
import { AndroidApplication, Utils } from "@nativescript/core"
|
||||
import { mapActions, mapState } from "vuex"
|
||||
|
||||
import EditRecipe from "./EditRecipe.vue"
|
||||
import ViewRecipe from "./ViewRecipe.vue"
|
||||
|
||||
import ActionDialog from "./modal/ActionDialog.vue"
|
||||
import ConfirmDialog from "./modal/ConfirmDialog.vue"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
|
||||
export default {
|
||||
props: [
|
||||
|
@ -261,7 +290,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
...mapActions(["setCurrentComponentAction", "deleteRecipeAction"]),
|
||||
initializePage() {
|
||||
onPageLoad() {
|
||||
this.filterFavorites
|
||||
? this.setComponent("Favorites")
|
||||
: this.filterTrylater
|
||||
|
@ -271,10 +300,46 @@ export default {
|
|||
: this.setComponent("EnRecipes")
|
||||
this.showFAB = true
|
||||
},
|
||||
|
||||
// HELPERS
|
||||
openSearch() {
|
||||
this.showSearch = true
|
||||
this.showFAB = false
|
||||
this.hijackLocalBackEvent()
|
||||
},
|
||||
closeSearch() {
|
||||
if (this.searchQuery) this.updateFilter()
|
||||
this.searchQuery = ""
|
||||
Utils.ad.dismissSoftInput()
|
||||
this.showSearch = false
|
||||
this.showFAB = true
|
||||
this.releaseLocalBackEvent()
|
||||
},
|
||||
setComponent(comp) {
|
||||
this.setCurrentComponentAction(comp)
|
||||
this.hijackGlobalBackEvent()
|
||||
},
|
||||
clearSearch() {
|
||||
if (this.searchQuery !== "") {
|
||||
this.updateFilter()
|
||||
}
|
||||
},
|
||||
formattedTime(time) {
|
||||
let t = time.split(":")
|
||||
let h = parseInt(t[0])
|
||||
let m = parseInt(t[1])
|
||||
return {
|
||||
time: h ? (m ? `${h}h ${m}m` : `${h}h`) : `${m}m`,
|
||||
duration: `${h}${m}`,
|
||||
}
|
||||
},
|
||||
onScroll(args) {
|
||||
args.scrollOffset
|
||||
? (this.viewIsScrolled = true)
|
||||
: (this.viewIsScrolled = false)
|
||||
},
|
||||
|
||||
// NAVIGATION HANDLERS
|
||||
hijackLocalBackEvent() {
|
||||
this.releaseGlobalBackEvent()
|
||||
AndroidApplication.on(
|
||||
|
@ -293,20 +358,49 @@ export default {
|
|||
args.cancel = true
|
||||
this.closeSearch()
|
||||
},
|
||||
closeSearch() {
|
||||
if (this.searchQuery) this.updateFilter()
|
||||
this.searchQuery = ""
|
||||
Utils.ad.dismissSoftInput()
|
||||
this.showSearch = false
|
||||
this.releaseLocalBackEvent()
|
||||
addRecipe() {
|
||||
this.showFAB = false
|
||||
this.releaseGlobalBackEvent()
|
||||
this.$navigateTo(EditRecipe, {
|
||||
// transition: {
|
||||
// name: "fade",
|
||||
// duration: 200,
|
||||
// curve: "easeOut",
|
||||
// },
|
||||
props: {
|
||||
selectedCategory: this.selectedCategory,
|
||||
openAppSettingsPage: this.openAppSettingsPage,
|
||||
filterFavorites: this.filterFavorites,
|
||||
},
|
||||
})
|
||||
},
|
||||
viewRecipe(item) {
|
||||
let index = this.recipes.indexOf(item)
|
||||
this.showFAB = false
|
||||
this.$navigateTo(ViewRecipe, {
|
||||
// transition: {
|
||||
// name: "fade",
|
||||
// duration: 200,
|
||||
// curve: "easeOut",
|
||||
// },
|
||||
props: {
|
||||
filterTrylater: this.filterTrylater,
|
||||
recipeIndex: index,
|
||||
recipeID: item.id,
|
||||
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
|
||||
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
// LIST HANDLERS
|
||||
sortDialog() {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.$showModal(ActionDialog, {
|
||||
props: {
|
||||
title: "Sort by",
|
||||
list: ["Natural order", "Title", "Duration", "Last modified"],
|
||||
height: "216", // 54*4
|
||||
height: "225",
|
||||
},
|
||||
}).then((action) => {
|
||||
if (action && action !== "Cancel" && this.sortType !== action) {
|
||||
|
@ -346,10 +440,6 @@ export default {
|
|||
break
|
||||
}
|
||||
},
|
||||
setComponent(comp) {
|
||||
this.setCurrentComponentAction(comp)
|
||||
this.hijackGlobalBackEvent()
|
||||
},
|
||||
updateFilter() {
|
||||
let listView = this.$refs.listView.nativeView
|
||||
setTimeout((e) => {
|
||||
|
@ -392,6 +482,8 @@ export default {
|
|||
this.deleteRecipe(index, recipeID)
|
||||
this.rightAction = false
|
||||
},
|
||||
|
||||
// DATA HANDLERS
|
||||
deleteRecipe(index, recipeID) {
|
||||
this.deletionDialogActive = true
|
||||
this.$showModal(ConfirmDialog, {
|
||||
|
@ -408,54 +500,6 @@ export default {
|
|||
this.deletionDialogActive = false
|
||||
})
|
||||
},
|
||||
|
||||
formattedTime(time) {
|
||||
let t = time.split(":")
|
||||
let h = parseInt(t[0])
|
||||
let m = parseInt(t[1])
|
||||
return {
|
||||
time: h ? (m ? `${h}h ${m}m` : `${h}h`) : `${m}m`,
|
||||
duration: `${h}${m}`,
|
||||
}
|
||||
},
|
||||
onScroll(args) {
|
||||
args.scrollOffset
|
||||
? (this.viewIsScrolled = true)
|
||||
: (this.viewIsScrolled = false)
|
||||
},
|
||||
addRecipe() {
|
||||
this.showFAB = false
|
||||
this.releaseGlobalBackEvent()
|
||||
this.$navigateTo(EditRecipe, {
|
||||
// transition: {
|
||||
// name: "fade",
|
||||
// duration: 200,
|
||||
// curve: "easeOut",
|
||||
// },
|
||||
props: {
|
||||
selectedCategory: this.selectedCategory,
|
||||
openAppSettingsPage: this.openAppSettingsPage,
|
||||
filterFavorites: this.filterFavorites,
|
||||
},
|
||||
})
|
||||
},
|
||||
viewRecipe({ item, index }) {
|
||||
this.showFAB = false
|
||||
this.$navigateTo(ViewRecipe, {
|
||||
// transition: {
|
||||
// name: "fade",
|
||||
// duration: 200,
|
||||
// curve: "easeOut",
|
||||
// },
|
||||
props: {
|
||||
filterTrylater: this.filterTrylater,
|
||||
recipeIndex: index,
|
||||
recipeID: item.id,
|
||||
hijackGlobalBackEvent: this.hijackGlobalBackEvent,
|
||||
releaseGlobalBackEvent: this.releaseGlobalBackEvent,
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.showFAB = true
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<Page @loaded="initializePage">
|
||||
<Page @loaded="onPageLoad">
|
||||
<ActionBar :flat="viewIsScrolled ? false : true">
|
||||
<GridLayout rows="*" columns="auto, *">
|
||||
<Label
|
||||
class="bx"
|
||||
<MDButton
|
||||
class="bx left"
|
||||
variant="text"
|
||||
:text="icon.menu"
|
||||
automationText="Back"
|
||||
@tap="showDrawer"
|
||||
|
@ -14,55 +15,59 @@
|
|||
</ActionBar>
|
||||
<ScrollView scrollBarIndicatorVisible="false" @scroll="onScroll">
|
||||
<StackLayout class="main-container">
|
||||
<Label text="Interface" class="group-header" />
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="option"
|
||||
@tap="selectThemes"
|
||||
>
|
||||
<Label verticalAlignment="center" class="bx" :text="icon.theme" />
|
||||
<StackLayout>
|
||||
<Label text="Interface" class="group-header orkm" />
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<MDRipple colSpan="2" @tap="selectThemes" />
|
||||
<Label
|
||||
col="0"
|
||||
verticalAlignment="center"
|
||||
class="bx"
|
||||
:text="icon.theme"
|
||||
/>
|
||||
<StackLayout col="1">
|
||||
<Label text="Theme" />
|
||||
<Label :text="appTheme" class="option-info" textWrap="true" />
|
||||
<Label :text="appTheme" class="info" textWrap="true" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
<StackLayout class="hr m-10"></StackLayout>
|
||||
<Label text="Database" class="group-header" />
|
||||
<StackLayout orientation="horizontal" class="option" @tap="backupCheck">
|
||||
<Label class="bx" :text="icon.export" />
|
||||
<StackLayout>
|
||||
<Label text="Database" class="group-header orkm" />
|
||||
<GridLayout columns="auto, *" class="option">
|
||||
<MDRipple colSpan="2" @tap="exportCheck" />
|
||||
<Label col="0" class="bx" :text="icon.export" />
|
||||
<StackLayout col="1">
|
||||
<Label text="Export a full backup" />
|
||||
<GridLayout
|
||||
class="progressContainer"
|
||||
v-if="backupInProgress"
|
||||
columns="*, 64"
|
||||
>
|
||||
<Progress col="0" :value="backupProgress" />
|
||||
<MDProgress
|
||||
col="0"
|
||||
:value="backupProgress"
|
||||
maxValue="100"
|
||||
></MDProgress>
|
||||
<Label col="1" :text="` ${backupProgress}%`" />
|
||||
</GridLayout>
|
||||
<Label
|
||||
v-else
|
||||
text="Generates a zip file that contains all your data. This file can be imported back."
|
||||
class="option-info"
|
||||
class="info"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
orientation="horizontal"
|
||||
class="option"
|
||||
@tap="restoreCheck"
|
||||
>
|
||||
<Label class="bx" :text="icon.import" />
|
||||
<StackLayout>
|
||||
</GridLayout>
|
||||
<GridLayout columns="auto, *" class="option"
|
||||
><MDRipple colSpan="2" @tap="importCheck" />
|
||||
<Label col="0" class="bx" :text="icon.import" />
|
||||
<StackLayout col="1">
|
||||
<Label text="Import from backup" />
|
||||
<Label
|
||||
text="Supports full backups exported by this app."
|
||||
class="option-info"
|
||||
class="info"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</Page>
|
||||
|
@ -83,14 +88,13 @@ import { Zip } from "@nativescript/zip"
|
|||
import * as Toast from "nativescript-toast"
|
||||
import * as Filepicker from "nativescript-plugin-filepicker"
|
||||
import Theme from "@nativescript/theme"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
|
||||
import ActionDialog from "./modal/ActionDialog.vue"
|
||||
import ConfirmDialog from "./modal/ConfirmDialog.vue"
|
||||
|
||||
import { Couchbase } from "nativescript-couchbase-plugin"
|
||||
import { mapState, mapActions } from "vuex"
|
||||
export default {
|
||||
props: [
|
||||
"highlight",
|
||||
"showDrawer",
|
||||
"restartApp",
|
||||
"hijackGlobalBackEvent",
|
||||
|
@ -121,22 +125,25 @@ export default {
|
|||
"importYieldUnitsAction",
|
||||
"importRecipesAction",
|
||||
]),
|
||||
initializePage() {
|
||||
onPageLoad() {
|
||||
this.setCurrentComponentAction("Settings")
|
||||
this.releaseGlobalBackEvent()
|
||||
},
|
||||
|
||||
// HELPERS
|
||||
onScroll(args) {
|
||||
args.scrollY
|
||||
? (this.viewIsScrolled = true)
|
||||
: (this.viewIsScrolled = false)
|
||||
},
|
||||
selectThemes(args) {
|
||||
this.highlight(args)
|
||||
|
||||
// THEME SELECTION
|
||||
selectThemes() {
|
||||
this.$showModal(ActionDialog, {
|
||||
props: {
|
||||
title: "Theme",
|
||||
list: ["Light", "Dark"],
|
||||
height: "108",
|
||||
height: "113",
|
||||
},
|
||||
}).then((action) => {
|
||||
if (action && action !== "Cancel" && this.appTheme !== action) {
|
||||
|
@ -159,53 +166,23 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
writeFile(file, data) {
|
||||
file.writeText(JSON.stringify(data))
|
||||
},
|
||||
BackupDataFiles(option) {
|
||||
const folder = path.join(knownFolders.documents().path, "EnRecipes")
|
||||
const EnRecipesFile = File.fromPath(path.join(folder, "EnRecipes.json"))
|
||||
let userCategoriesFile, userYieldUnitsFile
|
||||
if (this.userCategories.length) {
|
||||
userCategoriesFile = File.fromPath(
|
||||
path.join(folder, "userCategories.json")
|
||||
// EXPORT HANDLERS
|
||||
exportCheck() {
|
||||
if (!this.recipes.length) {
|
||||
Toast.makeText(
|
||||
"Add at least one recipe to perform a backup",
|
||||
"long"
|
||||
).show()
|
||||
} else {
|
||||
this.permissionCheck(
|
||||
this.permissionConfirmation,
|
||||
"EnRecipes requires storage permission in order to backup your data to this device.",
|
||||
this.exportBackup
|
||||
)
|
||||
}
|
||||
if (this.userYieldUnits.length) {
|
||||
userYieldUnitsFile = File.fromPath(
|
||||
path.join(folder, "userYieldUnits.json")
|
||||
)
|
||||
}
|
||||
switch (option) {
|
||||
case "create":
|
||||
this.writeFile(EnRecipesFile, this.recipes)
|
||||
this.userCategories.length &&
|
||||
this.writeFile(userCategoriesFile, this.userCategories)
|
||||
this.userYieldUnits.length &&
|
||||
this.writeFile(userYieldUnitsFile, this.userYieldUnits)
|
||||
break
|
||||
case "delete":
|
||||
EnRecipesFile.remove()
|
||||
this.userCategories.length && userCategoriesFile.remove()
|
||||
this.userYieldUnits.length && userYieldUnitsFile.remove()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
backupPermissionConfirmation() {
|
||||
return this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: "Grant permission",
|
||||
description:
|
||||
"EnRecipes requires storage permission in order to backup your data to this device",
|
||||
cancelButtonText: "NOT NOW",
|
||||
okButtonText: "CONTINUE",
|
||||
},
|
||||
})
|
||||
},
|
||||
backupData() {
|
||||
this.BackupDataFiles("create")
|
||||
exportBackup() {
|
||||
this.exportFiles("create")
|
||||
let date = new Date()
|
||||
let formattedDate =
|
||||
date.getFullYear() +
|
||||
|
@ -242,59 +219,70 @@ export default {
|
|||
"Backup file successfully saved to Downloads",
|
||||
"long"
|
||||
).show()
|
||||
this.BackupDataFiles("delete")
|
||||
this.exportFiles("delete")
|
||||
})
|
||||
},
|
||||
backupCheck(args) {
|
||||
let btn = args.object
|
||||
this.highlight(args)
|
||||
if (!this.recipes.length) {
|
||||
Toast.makeText(
|
||||
"Add at least one recipe to perform a backup",
|
||||
"long"
|
||||
).show()
|
||||
} else {
|
||||
this.permissionCheck(this.backupPermissionConfirmation, this.backupData)
|
||||
exportFiles(option) {
|
||||
const folder = path.join(knownFolders.documents().path, "EnRecipes")
|
||||
const EnRecipesFile = File.fromPath(path.join(folder, "EnRecipes.json"))
|
||||
let userCategoriesFile, userYieldUnitsFile
|
||||
if (this.userCategories.length) {
|
||||
userCategoriesFile = File.fromPath(
|
||||
path.join(folder, "userCategories.json")
|
||||
)
|
||||
}
|
||||
if (this.userYieldUnits.length) {
|
||||
userYieldUnitsFile = File.fromPath(
|
||||
path.join(folder, "userYieldUnits.json")
|
||||
)
|
||||
}
|
||||
switch (option) {
|
||||
case "create":
|
||||
this.writeDataToFile(EnRecipesFile, this.recipes)
|
||||
this.userCategories.length &&
|
||||
this.writeDataToFile(userCategoriesFile, this.userCategories)
|
||||
this.userYieldUnits.length &&
|
||||
this.writeDataToFile(userYieldUnitsFile, this.userYieldUnits)
|
||||
break
|
||||
case "delete":
|
||||
EnRecipesFile.remove()
|
||||
this.userCategories.length && userCategoriesFile.remove()
|
||||
this.userYieldUnits.length && userYieldUnitsFile.remove()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
restorePermissionConfirmation() {
|
||||
return this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: "Grant permission",
|
||||
description:
|
||||
"EnRecipes requires storage permission in order to restore your data from a previous backup.",
|
||||
cancelButtonText: "NOT NOW",
|
||||
okButtonText: "CONTINUE",
|
||||
},
|
||||
})
|
||||
writeDataToFile(file, data) {
|
||||
file.writeText(JSON.stringify(data))
|
||||
},
|
||||
restoreCheck(args) {
|
||||
let btn = args.object
|
||||
this.highlight(args)
|
||||
|
||||
// IMPORT HANDLERS
|
||||
importCheck() {
|
||||
this.permissionCheck(
|
||||
this.restorePermissionConfirmation,
|
||||
this.permissionConfirmation,
|
||||
"EnRecipes requires storage permission in order to restore your data from a previous backup.",
|
||||
this.openFilePicker
|
||||
)
|
||||
},
|
||||
openFilePicker() {
|
||||
let context = Filepicker.create({
|
||||
mode: "single", // use "multiple" for multiple selection
|
||||
Filepicker.create({
|
||||
mode: "single",
|
||||
extensions: ["zip"],
|
||||
})
|
||||
context.present().then((selection) => {
|
||||
Toast.makeText("Processing...").show()
|
||||
let result = selection[0]
|
||||
let zipPath = result
|
||||
let dest = knownFolders.documents().path
|
||||
this.validateZipContent(zipPath)
|
||||
})
|
||||
.present()
|
||||
.then((selection) => {
|
||||
Toast.makeText("Processing...").show()
|
||||
let result = selection[0]
|
||||
let zipPath = result
|
||||
let dest = knownFolders.documents().path
|
||||
this.validateZipContent(zipPath)
|
||||
})
|
||||
},
|
||||
importDataToDB(data, db, zipPath) {
|
||||
switch (db) {
|
||||
case "EnRecipesDB":
|
||||
this.copyImages(zipPath)
|
||||
this.importImages(zipPath)
|
||||
this.importRecipesAction(data)
|
||||
break
|
||||
case "userCategoriesDB":
|
||||
|
@ -307,7 +295,7 @@ export default {
|
|||
break
|
||||
}
|
||||
},
|
||||
isImportedDataValid(file) {
|
||||
isFileDataValid(file) {
|
||||
file.forEach((file, i) => {
|
||||
if (File.exists(file.path)) {
|
||||
File.fromPath(file.path)
|
||||
|
@ -329,7 +317,7 @@ export default {
|
|||
const userCategoriesFilePath = cacheFolderPath + "/userCategories.json"
|
||||
const userYieldUnitsFilePath = cacheFolderPath + "/userYieldUnits.json"
|
||||
if (Folder.exists(cacheFolderPath)) {
|
||||
this.isImportedDataValid([
|
||||
this.isFileDataValid([
|
||||
{
|
||||
zipPath,
|
||||
path: EnRecipesFilePath,
|
||||
|
@ -341,30 +329,33 @@ export default {
|
|||
} else {
|
||||
Folder.fromPath(extractedFolderPath).remove()
|
||||
Toast.makeText(
|
||||
"Zip modified externally or incorrect file",
|
||||
"Import failed. Backup file is incorrect or corrupt",
|
||||
"long"
|
||||
).show()
|
||||
}
|
||||
if (Folder.exists(cacheFolderPath + "/Images")) {
|
||||
this.copyImages(cacheFolderPath + "/Images")
|
||||
this.importImages(cacheFolderPath + "/Images")
|
||||
}
|
||||
})
|
||||
},
|
||||
copyImages(sourcePath) {
|
||||
importImages(sourcePath) {
|
||||
let dest = knownFolders.documents().path
|
||||
Zip.unzip({
|
||||
archive: sourcePath,
|
||||
directory: dest,
|
||||
overwrite: true,
|
||||
}).then((res) => {
|
||||
Toast.makeText("Import successful!", "long").show()
|
||||
Toast.makeText("Import successful", "long").show()
|
||||
this.$navigateBack()
|
||||
})
|
||||
},
|
||||
permissionCheck(confirmation, action) {
|
||||
|
||||
// PERMISSIONS HANDLER
|
||||
permissionCheck(confirmation, description, action) {
|
||||
if (!ApplicationSettings.getBoolean("storagePermissionAsked", false)) {
|
||||
confirmation().then((e) => {
|
||||
confirmation(description).then((e) => {
|
||||
if (e) {
|
||||
Permissions.request("storage").then((res) => {
|
||||
Permissions.request("photo").then((res) => {
|
||||
let status = res[Object.keys(res)[0]]
|
||||
if (status === "authorized") action()
|
||||
if (status !== "denied")
|
||||
|
@ -374,16 +365,26 @@ export default {
|
|||
}
|
||||
})
|
||||
} else {
|
||||
Permissions.request("storage").then((res) => {
|
||||
Permissions.check("photo").then((res) => {
|
||||
let status = res[Object.keys(res)[0]]
|
||||
if (status !== "authorized") {
|
||||
confirmation().then((e) => {
|
||||
confirmation(description).then((e) => {
|
||||
e && this.openAppSettingsPage()
|
||||
})
|
||||
} else action()
|
||||
})
|
||||
}
|
||||
},
|
||||
permissionConfirmation(description) {
|
||||
return this.$showModal(ConfirmDialog, {
|
||||
props: {
|
||||
title: "Grant permission",
|
||||
description,
|
||||
cancelButtonText: "NOT NOW",
|
||||
okButtonText: "CONTINUE",
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.appTheme = ApplicationSettings.getString("appTheme", "Light")
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<template>
|
||||
<Page @loaded="initializePage" @unloaded="unLoad">
|
||||
<Page @loaded="onPageLoad" @unloaded="onPageUnload">
|
||||
<ActionBar height="112" margin="0" flat="true">
|
||||
<GridLayout rows="64, 48" columns="auto, *, auto,auto, auto">
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
row="0"
|
||||
col="0"
|
||||
class="bx"
|
||||
|
@ -23,30 +24,36 @@
|
|||
verticalAlignment="bottom"
|
||||
/>
|
||||
</ScrollView>
|
||||
<Label
|
||||
row="0"
|
||||
col="3"
|
||||
class="bx"
|
||||
:text="recipe.isFavorite ? icon.heart : icon.heartOutline"
|
||||
@tap="toggleFavorite"
|
||||
/>
|
||||
<Label
|
||||
v-if="!filterTrylater"
|
||||
row="0"
|
||||
col="4"
|
||||
class="bx"
|
||||
:text="recipe.tried ? icon.trylaterOutline : icon.trylater"
|
||||
@tap="toggleTrylater"
|
||||
/>
|
||||
<Label
|
||||
v-if="!busy"
|
||||
row="0"
|
||||
col="2"
|
||||
class="bx"
|
||||
:text="icon.edit"
|
||||
@tap="editRecipe"
|
||||
/>
|
||||
<ActivityIndicator v-else row="0" col="2" :busy="busy" />
|
||||
<FlexboxLayout row="0" col="2" alignItems="center">
|
||||
<MDButton
|
||||
variant="text"
|
||||
v-if="!busy"
|
||||
class="bx"
|
||||
:text="icon.edit"
|
||||
@tap="editRecipe"
|
||||
/>
|
||||
<MDActivityIndicator v-else :busy="busy" />
|
||||
<MDButton
|
||||
variant="text"
|
||||
class="bx"
|
||||
:text="recipe.isFavorite ? icon.heart : icon.heartOutline"
|
||||
@tap="toggleFavorite"
|
||||
/>
|
||||
<MDButton
|
||||
variant="text"
|
||||
v-if="!filterTrylater"
|
||||
class="bx"
|
||||
:text="recipe.tried ? icon.trylaterOutline : icon.trylater"
|
||||
@tap="toggleTrylater"
|
||||
/>
|
||||
<MDButton
|
||||
variant="text"
|
||||
v-else
|
||||
class="bx"
|
||||
:text="icon.share"
|
||||
@tap="shareHandler"
|
||||
/>
|
||||
</FlexboxLayout>
|
||||
</GridLayout>
|
||||
</ActionBar>
|
||||
<AbsoluteLayout>
|
||||
|
@ -91,27 +98,26 @@
|
|||
:text="recipe.title"
|
||||
textWrap="true"
|
||||
/>
|
||||
<Label class="time">
|
||||
<FormattedString>
|
||||
<Span text="Time required:"></Span>
|
||||
<Span
|
||||
:text="` ${formattedTime(recipe.timeRequired)}`"
|
||||
></Span>
|
||||
</FormattedString>
|
||||
</Label>
|
||||
<StackLayout orientation="horizontal" class="time">
|
||||
<Label text="Time required:" />
|
||||
<Label :text="` ${formattedTime(recipe.timeRequired)}`" />
|
||||
</StackLayout>
|
||||
<GridLayout
|
||||
rows="auto, auto"
|
||||
columns="*, *"
|
||||
class="overviewContainer"
|
||||
>
|
||||
<StackLayout
|
||||
<GridLayout
|
||||
class="overviewItem"
|
||||
row="0"
|
||||
col="0"
|
||||
@tap="selectedTabIndex = 1"
|
||||
rows="auto, auto"
|
||||
columns="*"
|
||||
>
|
||||
<Label class="bx" :text="icon.item" />
|
||||
<MDRipple rowSpan="2" @tap="selectedTabIndex = 1" />
|
||||
<Label row="0" class="bx" :text="icon.item" />
|
||||
<Label
|
||||
row="1"
|
||||
class="itemCount"
|
||||
:text="
|
||||
`${recipe.ingredients.length} ${
|
||||
|
@ -122,15 +128,18 @@
|
|||
"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
</GridLayout>
|
||||
<GridLayout
|
||||
class="overviewItem"
|
||||
row="0"
|
||||
col="1"
|
||||
@tap="selectedTabIndex = 2"
|
||||
rows="auto, auto"
|
||||
columns="*"
|
||||
>
|
||||
<Label class="bx" :text="icon.step" />
|
||||
<MDRipple rowSpan="2" @tap="selectedTabIndex = 2" />
|
||||
<Label row="0" class="bx" :text="icon.step" />
|
||||
<Label
|
||||
row="1"
|
||||
class="itemCount"
|
||||
:text="
|
||||
`${recipe.instructions.length} ${
|
||||
|
@ -139,15 +148,18 @@
|
|||
"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
</GridLayout>
|
||||
<GridLayout
|
||||
class="overviewItem"
|
||||
row="1"
|
||||
col="0"
|
||||
@tap="selectedTabIndex = 3"
|
||||
rows="auto, auto"
|
||||
columns="*"
|
||||
>
|
||||
<Label class="bx" :text="icon.note" />
|
||||
<MDRipple rowSpan="2" @tap="selectedTabIndex = 3" />
|
||||
<Label row="0" class="bx" :text="icon.note" />
|
||||
<Label
|
||||
row="1"
|
||||
class="itemCount"
|
||||
:text="
|
||||
`${recipe.notes.length} ${
|
||||
|
@ -156,15 +168,18 @@
|
|||
"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
</GridLayout>
|
||||
<GridLayout
|
||||
class="overviewItem"
|
||||
row="1"
|
||||
col="1"
|
||||
@tap="selectedTabIndex = 4"
|
||||
rows="auto, auto"
|
||||
columns="*"
|
||||
>
|
||||
<Label class="bx" :text="icon.source" />
|
||||
<MDRipple rowSpan="2" @tap="selectedTabIndex = 4" />
|
||||
<Label row="0" class="bx" :text="icon.source" />
|
||||
<Label
|
||||
row="1"
|
||||
class="itemCount"
|
||||
:text="
|
||||
`${recipe.references.length} ${
|
||||
|
@ -175,7 +190,7 @@
|
|||
"
|
||||
textWrap="true"
|
||||
/>
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
|
@ -185,11 +200,11 @@
|
|||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<GridLayout
|
||||
v-if="!recipe.ingredients.length"
|
||||
rows="96, auto, *"
|
||||
rows="*, auto, *, 88"
|
||||
columns="*"
|
||||
class="emptyState"
|
||||
class="emptyStateContainer"
|
||||
>
|
||||
<StackLayout col="0" row="1" class="noResult">
|
||||
<StackLayout col="0" row="1" class="emptyState">
|
||||
<Label class="bx icon" :text="icon.item" textWrap="true" />
|
||||
<StackLayout orientation="horizontal" class="title orkm">
|
||||
<Label text="Use the " />
|
||||
|
@ -199,7 +214,7 @@
|
|||
<Label text="to add some ingredients" textWrap="true" />
|
||||
</StackLayout>
|
||||
</GridLayout>
|
||||
<StackLayout v-else padding="16 16 134">
|
||||
<StackLayout v-else padding="32 16 134">
|
||||
<AbsoluteLayout class="inputField">
|
||||
<TextField
|
||||
width="50%"
|
||||
|
@ -212,7 +227,7 @@
|
|||
:text="`Required ${recipe.yield.unit.toLowerCase()}`"
|
||||
/>
|
||||
</AbsoluteLayout>
|
||||
<StackLayout margin="24 0 16 0">
|
||||
<StackLayout margin="16 0">
|
||||
<Label
|
||||
class="title orkm"
|
||||
:text="
|
||||
|
@ -231,7 +246,7 @@
|
|||
v-if="filterTrylater"
|
||||
class="ingredient-check"
|
||||
checkPadding="16"
|
||||
:fillColor="`${isLightMode ? '#ff5200' : '#ff7043'}`"
|
||||
fillColor="#ff5200"
|
||||
:text="
|
||||
`${
|
||||
roundedQuantity(item.quantity)
|
||||
|
@ -264,11 +279,11 @@
|
|||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<GridLayout
|
||||
v-if="!recipe.instructions.length"
|
||||
rows="96, auto, *"
|
||||
rows="*, auto, *, 88"
|
||||
columns="*"
|
||||
class="emptyState"
|
||||
class="emptyStateContainer"
|
||||
>
|
||||
<StackLayout col="0" row="1" class="noResult">
|
||||
<StackLayout col="0" row="1" class="emptyState">
|
||||
<Label class="bx icon" :text="icon.step" textWrap="true" />
|
||||
<StackLayout orientation="horizontal" class="title orkm">
|
||||
<Label text="Use the " />
|
||||
|
@ -309,11 +324,11 @@
|
|||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<GridLayout
|
||||
v-if="!recipe.notes.length"
|
||||
rows="96, auto, *"
|
||||
rows="*, auto, *, 88"
|
||||
columns="*"
|
||||
class="emptyState"
|
||||
class="emptyStateContainer"
|
||||
>
|
||||
<StackLayout col="0" row="1" class="noResult">
|
||||
<StackLayout col="0" row="1" class="emptyState">
|
||||
<Label class="bx icon" :text="icon.note" textWrap="true" />
|
||||
<StackLayout orientation="horizontal" class="title orkm">
|
||||
<Label text="Use the " />
|
||||
|
@ -351,11 +366,11 @@
|
|||
<ScrollView scrollBarIndicatorVisible="false">
|
||||
<GridLayout
|
||||
v-if="!recipe.references.length"
|
||||
rows="96, auto, *"
|
||||
rows="*, auto, *, 88"
|
||||
columns="*"
|
||||
class="emptyState"
|
||||
class="emptyStateContainer"
|
||||
>
|
||||
<StackLayout col="0" row="1" class="noResult">
|
||||
<StackLayout col="0" row="1" class="emptyState">
|
||||
<Label class="bx icon" :text="icon.source" textWrap="true" />
|
||||
<StackLayout orientation="horizontal" class="title orkm">
|
||||
<Label text="Use the " />
|
||||
|
@ -375,8 +390,8 @@
|
|||
columns="*, auto"
|
||||
class="referenceItem"
|
||||
androidElevation="1"
|
||||
@longPress="copyURL($event, reference)"
|
||||
>
|
||||
<MDRipple colSpan="3" @tap="copyURL(reference)" />
|
||||
<Label
|
||||
col="0"
|
||||
verticalAlignment="center"
|
||||
|
@ -384,11 +399,13 @@
|
|||
:text="reference"
|
||||
textWrap="false"
|
||||
/>
|
||||
<Label
|
||||
<MDButton
|
||||
variant="text"
|
||||
automationText="openURL"
|
||||
col="1"
|
||||
class="bx"
|
||||
:text="icon.source"
|
||||
@tap="openURL($event, reference)"
|
||||
@tap="openURL(reference)"
|
||||
/>
|
||||
</GridLayout>
|
||||
<Label
|
||||
|
@ -402,22 +419,20 @@
|
|||
</ScrollView>
|
||||
</TabViewItem>
|
||||
</TabView>
|
||||
<GridLayout id="btnFabContainer" rows="*,auto" columns="*,auto">
|
||||
<Label
|
||||
<GridLayout id="btnFabContainer" rows="*, auto" columns="*, auto">
|
||||
<MDFloatingActionButton
|
||||
row="1"
|
||||
col="1"
|
||||
class="bx fab-button"
|
||||
:text="icon.check"
|
||||
src="res://check"
|
||||
@tap="recipeTried"
|
||||
v-if="filterTrylater"
|
||||
/>
|
||||
<transition name="dolly" appear>
|
||||
<Label
|
||||
<MDFloatingActionButton
|
||||
row="1"
|
||||
col="1"
|
||||
class="bx fab-button"
|
||||
:text="icon.share"
|
||||
@tap="shareRecipe"
|
||||
src="res://share"
|
||||
@tap="shareHandler"
|
||||
v-if="!filterTrylater && showFab"
|
||||
/>
|
||||
</transition>
|
||||
|
@ -428,22 +443,24 @@
|
|||
|
||||
<script>
|
||||
import {
|
||||
Screen,
|
||||
Utils,
|
||||
ImageSource,
|
||||
Color,
|
||||
Device,
|
||||
File,
|
||||
Color,
|
||||
knownFolders,
|
||||
path,
|
||||
ImageSource,
|
||||
Screen,
|
||||
Utils,
|
||||
} from "@nativescript/core"
|
||||
import { Feedback, FeedbackType, FeedbackPosition } from "nativescript-feedback"
|
||||
import * as Toast from "nativescript-toast"
|
||||
import * as SocialShare from "nativescript-social-share-ns-7"
|
||||
import * as SocialShare from "@nativescript/social-share"
|
||||
import { setText } from "nativescript-clipboard"
|
||||
import { Application } from "@nativescript/core"
|
||||
|
||||
import { mapState, mapActions } from "vuex"
|
||||
import { mapActions, mapState } from "vuex"
|
||||
|
||||
import EditRecipe from "./EditRecipe.vue"
|
||||
import ShareChooser from "./modal/ShareChooser.vue"
|
||||
|
||||
let feedback = new Feedback()
|
||||
|
||||
|
@ -480,7 +497,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
...mapActions(["toggleStateAction", "setCurrentComponentAction"]),
|
||||
initializePage() {
|
||||
onPageLoad() {
|
||||
this.releaseGlobalBackEvent()
|
||||
this.busy = false
|
||||
setTimeout((e) => {
|
||||
|
@ -488,8 +505,14 @@ export default {
|
|||
}, 500)
|
||||
this.yieldMultiplier = this.recipe.yield.quantity
|
||||
this.showFab = true
|
||||
this.toggleScreenOn(true)
|
||||
this.keepScreenOn(true)
|
||||
},
|
||||
onPageUnload() {
|
||||
feedback.hide()
|
||||
this.keepScreenOn(false)
|
||||
},
|
||||
|
||||
// HELPERS
|
||||
niceDates(time) {
|
||||
let lastTried = new Date(time).getTime()
|
||||
let now = new Date().getTime()
|
||||
|
@ -509,26 +532,15 @@ export default {
|
|||
selectedIndexChange(args) {
|
||||
this.selectedTabIndex = args.object.selectedIndex
|
||||
},
|
||||
showInfo() {
|
||||
showLastTried() {
|
||||
feedback.show({
|
||||
title: `You tried this recipe ${this.niceDates(
|
||||
this.recipe.lastTried
|
||||
)}!`,
|
||||
titleColor: new Color(`${this.isLightMode ? "#fff" : "#111"}`),
|
||||
backgroundColor: new Color(
|
||||
`${this.isLightMode ? "#ff5200" : "#ff7043"}`
|
||||
),
|
||||
titleColor: new Color(`${this.isLightMode ? "#f1f3f5" : "#212529"}`),
|
||||
backgroundColor: new Color("#ff5200"),
|
||||
})
|
||||
},
|
||||
unLoad() {
|
||||
feedback.hide()
|
||||
this.toggleScreenOn(false)
|
||||
},
|
||||
highlight(args) {
|
||||
let temp = args.object.className
|
||||
args.object.className = `${temp} option-highlight`
|
||||
setTimeout(() => (args.object.className = temp), 100)
|
||||
},
|
||||
roundedQuantity(quantity) {
|
||||
return (
|
||||
Math.round(
|
||||
|
@ -538,6 +550,32 @@ export default {
|
|||
) / 100
|
||||
)
|
||||
},
|
||||
keepScreenOn(boolean) {
|
||||
let activity =
|
||||
Application.android.foregroundActivity ||
|
||||
Application.android.startActivity
|
||||
let window = activity.getWindow()
|
||||
if (boolean)
|
||||
window.addFlags(
|
||||
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
)
|
||||
else
|
||||
window.clearFlags(
|
||||
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
)
|
||||
},
|
||||
formattedTime(time) {
|
||||
let t = time.split(":")
|
||||
let h = parseInt(t[0])
|
||||
let m = parseInt(t[1])
|
||||
return h ? (m ? `${h}h ${m}m` : `${h}h`) : `${m}m`
|
||||
},
|
||||
isValidURL(string) {
|
||||
let pattern = new RegExp("^https?|www", "ig")
|
||||
return pattern.test(string)
|
||||
},
|
||||
|
||||
// NAVIGATION HANDLERS
|
||||
editRecipe() {
|
||||
this.showFab = false
|
||||
this.busy = true
|
||||
|
@ -554,6 +592,47 @@ export default {
|
|||
// backstackVisible: false,
|
||||
})
|
||||
},
|
||||
|
||||
// SHARE ACTION
|
||||
shareHandler() {
|
||||
if (this.recipe.imageSrc) {
|
||||
this.$showModal(ShareChooser, {
|
||||
props: {
|
||||
title: "Share",
|
||||
},
|
||||
}).then((result) => {
|
||||
switch (result) {
|
||||
case "photo":
|
||||
// let cacheFilePath = path.join(
|
||||
// knownFolders.temp().path,
|
||||
// `${this.recipe.title}.jpg`
|
||||
// )
|
||||
// if (!File.exists(cacheFilePath)) {
|
||||
// File.fromPath(cacheFilePath).writeSync(
|
||||
// File.fromPath(this.recipe.imageSrc).readSync()
|
||||
// )
|
||||
// }
|
||||
// let shareFile = new ShareFile()
|
||||
// shareFile.open({
|
||||
// path: cacheFilePath,
|
||||
// title: "Share recipe photo using",
|
||||
// })
|
||||
ImageSource.fromFile(this.recipe.imageSrc).then((res) => {
|
||||
SocialShare.shareImage(res, "Share recipe photo using")
|
||||
})
|
||||
break
|
||||
case "recipe":
|
||||
this.shareRecipe()
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.shareRecipe()
|
||||
}
|
||||
},
|
||||
shareRecipe() {
|
||||
let overview = `${
|
||||
this.recipe.title
|
||||
|
@ -564,9 +643,11 @@ export default {
|
|||
this.yieldMultiplier
|
||||
} ${this.recipe.yield.unit.toLowerCase()}\n\n`
|
||||
this.recipe.ingredients.forEach((e) => {
|
||||
ingredients += `- ${this.roundedQuantity(e.quantity)} ${e.unit} ${
|
||||
e.item
|
||||
}\n`
|
||||
ingredients += `- ${
|
||||
e.quantity
|
||||
? this.roundedQuantity(e.quantity) + " " + e.unit + " "
|
||||
: ""
|
||||
}${e.item}\n`
|
||||
})
|
||||
shareContent += ingredients
|
||||
}
|
||||
|
@ -592,15 +673,14 @@ export default {
|
|||
shareContent += references
|
||||
}
|
||||
let sharenote =
|
||||
"\nCreated and shared via EnRecipes.\nDownload the app on f-droid: https://www.vishnuraghav.com/"
|
||||
"\nCreated and shared via EnRecipes.\nGet it on F-Droid."
|
||||
|
||||
shareContent += sharenote
|
||||
|
||||
SocialShare.shareText(
|
||||
shareContent,
|
||||
"How would you like to share this recipe?"
|
||||
)
|
||||
SocialShare.shareText(shareContent, "Share recipe using")
|
||||
},
|
||||
|
||||
// DATA HANDLERS
|
||||
toggle(key, setDate) {
|
||||
this.toggleStateAction({
|
||||
index: this.recipeIndex,
|
||||
|
@ -624,53 +704,32 @@ export default {
|
|||
: this.filterTrylater
|
||||
? Toast.makeText("You tried this recipe").show()
|
||||
: Toast.makeText("Removed from Try later").show()
|
||||
// : Toast.makeText("You tried this recipe").show()
|
||||
this.toggle("tried")
|
||||
},
|
||||
recipeTried() {
|
||||
this.toggle("tried", true)
|
||||
this.$navigateBack()
|
||||
},
|
||||
formattedTime(time) {
|
||||
let t = time.split(":")
|
||||
let h = parseInt(t[0])
|
||||
let m = parseInt(t[1])
|
||||
return h ? (m ? `${h}h ${m}m` : `${h}h`) : `${m}m`
|
||||
},
|
||||
isValidURL(string) {
|
||||
let pattern = new RegExp("^https?|www", "ig")
|
||||
return pattern.test(string)
|
||||
},
|
||||
openURL(args, url) {
|
||||
// this.highlight(args)
|
||||
|
||||
// URL ACTION
|
||||
openURL(url) {
|
||||
Utils.openUrl(url)
|
||||
},
|
||||
copyURL(args, url) {
|
||||
copyURL(url) {
|
||||
setText(url).then((e) => {
|
||||
Toast.makeText("URL Copied").show()
|
||||
})
|
||||
},
|
||||
toggleScreenOn(boolean) {
|
||||
let activity =
|
||||
Application.android.foregroundActivity ||
|
||||
Application.android.startActivity
|
||||
let window = activity.getWindow()
|
||||
if (boolean)
|
||||
window.addFlags(
|
||||
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
)
|
||||
else
|
||||
window.clearFlags(
|
||||
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.recipe = this.recipes.filter((e) => e.id === this.recipeID)[0]
|
||||
},
|
||||
mounted() {
|
||||
this.showFab = true
|
||||
setTimeout((e) => this.recipe.tried && this.showInfo(), 500)
|
||||
setTimeout(
|
||||
(e) => this.recipe.tried && this.recipe.lastTried && this.showLastTried(),
|
||||
500
|
||||
)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -2,26 +2,33 @@
|
|||
<Page>
|
||||
<StackLayout class="dialogContainer" :class="appTheme">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<ListView
|
||||
width="100%"
|
||||
:height="height"
|
||||
for="item in list"
|
||||
@itemTap="tapAction"
|
||||
separatorColor="transparent"
|
||||
>
|
||||
<v-template>
|
||||
<Label class="actionItem" :text="item" />
|
||||
</v-template>
|
||||
</ListView>
|
||||
<ScrollView width="100%" :height="height ? height : screenHeight - 320">
|
||||
<StackLayout>
|
||||
<MDButton
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
class="actionItem"
|
||||
variant="text"
|
||||
:rippleColor="rippleColor"
|
||||
:text="item"
|
||||
@loaded="onLabelLoaded"
|
||||
@tap="tapAction(item)"
|
||||
/>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
<GridLayout rows="auto" columns="auto, *, auto" class="actionsContainer">
|
||||
<Label
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
v-if="action"
|
||||
col="0"
|
||||
class="action orkm pull-left"
|
||||
:text="action"
|
||||
@tap="$modal.close(action)"
|
||||
/>
|
||||
<Label
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
col="2"
|
||||
class="action orkm pull-right"
|
||||
text="CANCEL"
|
||||
|
@ -33,18 +40,29 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { Application } from "@nativescript/core"
|
||||
import { Application, Screen } from "@nativescript/core"
|
||||
export default {
|
||||
props: ["title", "list", "height", "action"],
|
||||
computed: {
|
||||
appTheme() {
|
||||
return Application.systemAppearance()
|
||||
},
|
||||
rippleColor() {
|
||||
return this.appTheme == "light"
|
||||
? "rgba(134,142,150,0.2)"
|
||||
: "rgba(206,212,218,0.1)"
|
||||
},
|
||||
screenHeight() {
|
||||
return Math.round(Screen.mainScreen.heightDIPs)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
tapAction({ item }) {
|
||||
tapAction(item) {
|
||||
this.$modal.close(item)
|
||||
},
|
||||
onLabelLoaded(args) {
|
||||
args.object.android.setGravity(16)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<Page>
|
||||
<StackLayout class="dialogContainer" :class="isLightMode">
|
||||
<StackLayout class="dialogContainer" :class="appTheme">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<Label
|
||||
v-if="description"
|
||||
|
@ -8,19 +8,19 @@
|
|||
:text="description"
|
||||
textWrap="true"
|
||||
/>
|
||||
<GridLayout
|
||||
rows="auto"
|
||||
columns="*, auto, 32, auto"
|
||||
class="actionsContainer"
|
||||
>
|
||||
<Label
|
||||
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
col="1"
|
||||
class="action orkm"
|
||||
:text="cancelButtonText"
|
||||
@tap="$modal.close(false)"
|
||||
/>
|
||||
<Label
|
||||
col="3"
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
col="2"
|
||||
class="action orkm"
|
||||
:text="okButtonText"
|
||||
@tap="$modal.close(true)"
|
||||
|
@ -35,9 +35,14 @@ import { Application } from "@nativescript/core"
|
|||
export default {
|
||||
props: ["title", "description", "cancelButtonText", "okButtonText"],
|
||||
computed: {
|
||||
isLightMode() {
|
||||
appTheme() {
|
||||
return Application.systemAppearance()
|
||||
},
|
||||
rippleColor() {
|
||||
return this.appTheme == "light"
|
||||
? "rgba(134,142,150,0.2)"
|
||||
: "rgba(206,212,218,0.1)"
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<Page>
|
||||
<StackLayout class="dialogContainer" :class="isLightMode">
|
||||
<StackLayout class="dialogContainer" :class="appTheme">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<StackLayout
|
||||
class="dialogListPicker"
|
||||
|
@ -13,12 +13,6 @@
|
|||
:selectedIndex="hrIndex"
|
||||
@selectedIndexChange="setHrs"
|
||||
></ListPicker>
|
||||
<Label
|
||||
verticalAlignment="center"
|
||||
class="okrb"
|
||||
text=":"
|
||||
textWrap="false"
|
||||
/>
|
||||
<ListPicker
|
||||
ref="minPicker"
|
||||
:items="mins"
|
||||
|
@ -26,19 +20,19 @@
|
|||
@selectedIndexChange="setMins"
|
||||
></ListPicker>
|
||||
</StackLayout>
|
||||
<GridLayout
|
||||
rows="auto"
|
||||
columns="*, auto, 32, auto"
|
||||
class="actionsContainer"
|
||||
>
|
||||
<Label
|
||||
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
col="1"
|
||||
class="action orkm"
|
||||
text="CANCEL"
|
||||
@tap="$modal.close(false)"
|
||||
/>
|
||||
<Label
|
||||
col="3"
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
col="2"
|
||||
class="action orkm"
|
||||
:text="action"
|
||||
@tap="$modal.close(selectedTime)"
|
||||
|
@ -55,52 +49,52 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
hrs: [
|
||||
"00",
|
||||
"01",
|
||||
"02",
|
||||
"03",
|
||||
"04",
|
||||
"05",
|
||||
"06",
|
||||
"07",
|
||||
"08",
|
||||
"09",
|
||||
"10",
|
||||
"11",
|
||||
"12",
|
||||
"13",
|
||||
"14",
|
||||
"15",
|
||||
"16",
|
||||
"17",
|
||||
"18",
|
||||
"19",
|
||||
"20",
|
||||
"21",
|
||||
"22",
|
||||
"23",
|
||||
"0h",
|
||||
"1h",
|
||||
"2h",
|
||||
"3h",
|
||||
"4h",
|
||||
"5h",
|
||||
"6h",
|
||||
"7h",
|
||||
"8h",
|
||||
"9h",
|
||||
"10h",
|
||||
"11h",
|
||||
"12h",
|
||||
"13h",
|
||||
"14h",
|
||||
"15h",
|
||||
"16h",
|
||||
"17h",
|
||||
"18h",
|
||||
"19h",
|
||||
"20h",
|
||||
"21h",
|
||||
"22h",
|
||||
"23h",
|
||||
],
|
||||
mins: [
|
||||
"00",
|
||||
"01",
|
||||
"02",
|
||||
"03",
|
||||
"04",
|
||||
"05",
|
||||
"06",
|
||||
"07",
|
||||
"08",
|
||||
"09",
|
||||
"10",
|
||||
"15",
|
||||
"20",
|
||||
"25",
|
||||
"30",
|
||||
"35",
|
||||
"40",
|
||||
"45",
|
||||
"50",
|
||||
"55",
|
||||
"0m",
|
||||
"1m",
|
||||
"2m",
|
||||
"3m",
|
||||
"4m",
|
||||
"5m",
|
||||
"6m",
|
||||
"7m",
|
||||
"8m",
|
||||
"9m",
|
||||
"10m",
|
||||
"15m",
|
||||
"20m",
|
||||
"25m",
|
||||
"30m",
|
||||
"35m",
|
||||
"40m",
|
||||
"45m",
|
||||
"50m",
|
||||
"55m",
|
||||
],
|
||||
selectedHrs: "00",
|
||||
selectedMins: "00",
|
||||
|
@ -108,24 +102,37 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
hrIndex() {
|
||||
return this.hrs.indexOf(this.selectedHr)
|
||||
let hr = this.selectedHr
|
||||
if (hr.charAt(0) == "0") hr = hr.slice(-1) + "h"
|
||||
else hr = hr + "h"
|
||||
return this.hrs.indexOf(hr)
|
||||
},
|
||||
minIndex() {
|
||||
return this.mins.indexOf(this.selectedMin)
|
||||
let min = this.selectedMin
|
||||
if (min.charAt(0) == "0") min = min.slice(-1) + "m"
|
||||
else min = min + "m"
|
||||
return this.mins.indexOf(min)
|
||||
},
|
||||
isLightMode() {
|
||||
appTheme() {
|
||||
return Application.systemAppearance()
|
||||
},
|
||||
rippleColor() {
|
||||
return this.appTheme == "light"
|
||||
? "rgba(134,142,150,0.2)"
|
||||
: "rgba(206,212,218,0.1)"
|
||||
},
|
||||
selectedTime() {
|
||||
return this.selectedHrs + ":" + this.selectedMins
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setHrs(args) {
|
||||
this.selectedHrs = this.hrs[args.object.selectedIndex]
|
||||
let hr = "0" + this.hrs[args.object.selectedIndex]
|
||||
this.selectedHrs = hr.slice(-3).slice(0, -1)
|
||||
},
|
||||
setMins(args) {
|
||||
this.selectedMins = this.mins[args.object.selectedIndex]
|
||||
let min = "0" + this.mins[args.object.selectedIndex]
|
||||
this.selectedMins = min.slice(-3).slice(0, -1)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
<template>
|
||||
<Page>
|
||||
<StackLayout class="dialogContainer" :class="isLightMode">
|
||||
<StackLayout class="dialogContainer" :class="appTheme">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<StackLayout class="dialogInput">
|
||||
<TextField
|
||||
@loaded="focusField"
|
||||
:hint="hint"
|
||||
:hint="hint ? hint : ''"
|
||||
v-model="category"
|
||||
autocapitalizationType="words"
|
||||
/>
|
||||
</StackLayout>
|
||||
<GridLayout
|
||||
rows="auto"
|
||||
columns="*, auto, 32, auto"
|
||||
class="actionsContainer"
|
||||
>
|
||||
<Label
|
||||
<GridLayout rows="auto" columns="*, auto, auto" class="actionsContainer">
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
col="1"
|
||||
class="action orkm"
|
||||
text="CANCEL"
|
||||
@tap="$modal.close(false)"
|
||||
/>
|
||||
<Label
|
||||
col="3"
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
variant="text"
|
||||
col="2"
|
||||
class="action orkm"
|
||||
:text="action"
|
||||
@tap="$modal.close(category)"
|
||||
|
@ -35,16 +35,21 @@
|
|||
<script>
|
||||
import { Application, Utils } from "@nativescript/core"
|
||||
export default {
|
||||
props: ["title", "hint", "action"],
|
||||
props: ["title", "hint", "text", "action"],
|
||||
data() {
|
||||
return {
|
||||
category: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isLightMode() {
|
||||
appTheme() {
|
||||
return Application.systemAppearance()
|
||||
},
|
||||
rippleColor() {
|
||||
return this.appTheme == "light"
|
||||
? "rgba(134,142,150,0.2)"
|
||||
: "rgba(206,212,218,0.1)"
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
focusField(args) {
|
||||
|
@ -52,5 +57,10 @@ export default {
|
|||
setTimeout((e) => Utils.ad.showSoftInput(args.object.android), 1)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.text) {
|
||||
this.category = this.text
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
49
app/components/modal/ShareChooser.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<Page>
|
||||
<StackLayout class="dialogContainer" :class="appTheme">
|
||||
<Label class="dialogTitle orkm" :text="title" />
|
||||
<GridLayout rows="auto, auto" columns="*" class="actionsContainer">
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
:backgroundColor="backgroundColor"
|
||||
row="0"
|
||||
class="actionIcon"
|
||||
src="res://photo"
|
||||
text="Photo"
|
||||
@tap="$modal.close('photo')"
|
||||
/>
|
||||
<MDButton
|
||||
:rippleColor="rippleColor"
|
||||
:backgroundColor="backgroundColor"
|
||||
row="1"
|
||||
class="actionIcon"
|
||||
src="res://detail"
|
||||
text="Recipe"
|
||||
@tap="$modal.close('recipe')"
|
||||
/>
|
||||
</GridLayout>
|
||||
</StackLayout>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Application } from "@nativescript/core"
|
||||
import { mapState } from "vuex"
|
||||
export default {
|
||||
props: ["title"],
|
||||
computed: {
|
||||
...mapState(["icon"]),
|
||||
appTheme() {
|
||||
return Application.systemAppearance()
|
||||
},
|
||||
rippleColor() {
|
||||
return this.appTheme == "light"
|
||||
? "rgba(134,142,150,0.2)"
|
||||
: "rgba(206,212,218,0.1)"
|
||||
},
|
||||
backgroundColor() {
|
||||
return this.appTheme == "light" ? "#fff" : "#343a40"
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
18
app/main.js
|
@ -5,6 +5,24 @@ import store from "./store"
|
|||
import RadListView from "nativescript-ui-listview/vue"
|
||||
Vue.use(RadListView)
|
||||
|
||||
import ButtonPlugin from "@nativescript-community/ui-material-button/vue"
|
||||
Vue.use(ButtonPlugin)
|
||||
|
||||
import ActivityIndicatorPlugin from "@nativescript-community/ui-material-activityindicator/vue"
|
||||
Vue.use(ActivityIndicatorPlugin)
|
||||
|
||||
import RipplePlugin from "@nativescript-community/ui-material-ripple/vue"
|
||||
Vue.use(RipplePlugin)
|
||||
|
||||
import FloatingActionButtonPlugin from "@nativescript-community/ui-material-floatingactionbutton/vue"
|
||||
Vue.use(FloatingActionButtonPlugin)
|
||||
|
||||
import ProgressPlugin from "@nativescript-community/ui-material-progress/vue"
|
||||
Vue.use(ProgressPlugin)
|
||||
|
||||
// import SpeedDialPlugin from "@nativescript-community/ui-material-speeddial/vue"
|
||||
// Vue.use(SpeedDialPlugin)
|
||||
|
||||
Vue.registerElement(
|
||||
"RadSideDrawer",
|
||||
() => require("nativescript-ui-sidedrawer").RadSideDrawer
|
||||
|
|
|
@ -147,6 +147,7 @@ export default new Vuex.Store({
|
|||
heart: "\ued36",
|
||||
heartOutline: "\uea6c",
|
||||
label: "\ued51",
|
||||
labelOutline: "\uea8b",
|
||||
cog: "\ueca6",
|
||||
info: "\ued49",
|
||||
menu: "\ueac1",
|
||||
|
@ -165,6 +166,7 @@ export default new Vuex.Store({
|
|||
theme: "\uecaa",
|
||||
link: "\ueaa0",
|
||||
file: "\ued02",
|
||||
detail: "\ue9f9",
|
||||
user: "\uee33",
|
||||
trash: "\uee26",
|
||||
donate: "\ueb4f",
|
||||
|
@ -185,9 +187,8 @@ export default new Vuex.Store({
|
|||
},
|
||||
mutations: {
|
||||
initializeRecipes(state) {
|
||||
let a = EnRecipesDB.query({ select: [] })
|
||||
a.forEach((e) => {
|
||||
state.recipes.push(e)
|
||||
EnRecipesDB.query({ select: [] }).forEach((recipe) => {
|
||||
state.recipes.push(recipe)
|
||||
})
|
||||
},
|
||||
initializeCategories(state) {
|
||||
|
|
|
@ -1,17 +1,51 @@
|
|||
require("tns-core-modules/globals")
|
||||
import { ImageSource, ImageAsset } from "@nativescript/core"
|
||||
import { ImageSource } from "@nativescript/core"
|
||||
var ImageCropper = require("nativescript-imagecropper").ImageCropper
|
||||
|
||||
global.onmessage = function({ data }) {
|
||||
console.log(data)
|
||||
let imgSavedToPath = data.imgSavedToPath
|
||||
let imgAsset = new ImageAsset(data.imgFile)
|
||||
imgAsset.options = {
|
||||
width: 1200,
|
||||
height: 1200,
|
||||
keepAspectRatio: true,
|
||||
}
|
||||
ImageSource.fromAsset(imgAsset).then((imgData) => {
|
||||
if (imgData.saveToFile(imgSavedToPath, "jpg", 75)) {
|
||||
global.postMessage("savedToFile")
|
||||
}
|
||||
let imgPath = data.imgPath
|
||||
let screenWidth = data.screenWidth
|
||||
let toolbarTextColor = data.toolbarTextColor
|
||||
let toolbarColor = data.toolbarColor
|
||||
|
||||
ImageSource.fromFile(imgPath).then((image) => {
|
||||
ImageCropper.prototype
|
||||
.show(
|
||||
image,
|
||||
{
|
||||
width: screenWidth,
|
||||
height: screenWidth,
|
||||
// compressionQuality: 75,
|
||||
},
|
||||
{
|
||||
hideBottomControls: true,
|
||||
toolbarTitle: "Crop photo",
|
||||
statusBarColor: "#ff5200",
|
||||
toolbarTextColor,
|
||||
toolbarColor,
|
||||
cropFrameColor: "#ff5200",
|
||||
}
|
||||
)
|
||||
.then((cropped) => {
|
||||
if (cropped.image.saveToFile(imgSavedToPath, "jpg", 75))
|
||||
global.postMessage("savedToFile")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// global.onmessage = function({ data }) {
|
||||
// let imgSavedToPath = data.imgSavedToPath
|
||||
// let imgAsset = new ImageAsset(data.imgFile)
|
||||
// imgAsset.options = {
|
||||
// width: 1200,
|
||||
// height: 1200,
|
||||
// keepAspectRatio: true,
|
||||
// }
|
||||
// ImageSource.fromAsset(imgAsset).then((imgData) => {
|
||||
// if (imgData.saveToFile(imgSavedToPath, "jpg", 75)) {
|
||||
// global.postMessage("savedToFile")
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
|
2490
package-lock.json
generated
14
package.json
|
@ -9,17 +9,22 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@nativescript-community/perms": "^2.1.1",
|
||||
"@nativescript-community/ui-material-activityindicator": "^5.0.30",
|
||||
"@nativescript-community/ui-material-button": "^5.1.0",
|
||||
"@nativescript-community/ui-material-floatingactionbutton": "^5.0.30",
|
||||
"@nativescript-community/ui-material-progress": "^5.0.30",
|
||||
"@nativescript-community/ui-material-ripple": "^5.0.30",
|
||||
"@nativescript/core": "~7.0.0",
|
||||
"@nativescript/imagepicker": "^1.0.0",
|
||||
"@nativescript/social-share": "^2.0.1",
|
||||
"@nativescript/theme": "^3.0.0",
|
||||
"@nativescript/webpack": "3.0.0",
|
||||
"@nativescript/zip": "^5.0.0",
|
||||
"@nstudio/nativescript-checkbox": "^2.0.4",
|
||||
"nativescript-clipboard": "^2.0.0",
|
||||
"nativescript-couchbase-plugin": "^0.9.6",
|
||||
"nativescript-feedback": "^2.0.0",
|
||||
"nativescript-mediafilepicker": "^4.0.1",
|
||||
"nativescript-imagecropper": "^4.0.1",
|
||||
"nativescript-plugin-filepicker": "^1.0.0",
|
||||
"nativescript-social-share-ns-7": "^11.6.0",
|
||||
"nativescript-toast": "^2.0.0",
|
||||
"nativescript-ui-listview": "^9.0.4",
|
||||
"nativescript-ui-sidedrawer": "^9.0.3",
|
||||
|
@ -34,7 +39,8 @@
|
|||
"babel-loader": "^8.1.0",
|
||||
"nativescript-vue-template-compiler": "^2.6.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"vue-loader": "^15.9.1"
|
||||
"vue-loader": "^15.9.1",
|
||||
"@nativescript/webpack": "~3.0.0"
|
||||
},
|
||||
"main": "main"
|
||||
}
|
||||
|
|