FIX: Fixed performanc bug in paragraph support.

* apparently, if you do an ngFor on the result of function call that
    returns a new array each time its called, it will screw up the template
    watching code such that every action will necessitate a rerendering
    of the information even if it hasn't changed.
This commit is contained in:
walljm 2018-03-03 21:13:51 -05:00
parent 5a54a1a803
commit 7a0bc2f2b2
17 changed files with 1700 additions and 744 deletions

File diff suppressed because one or more lines are too long

View File

@ -67,35 +67,35 @@
<feature name="SplashScreen">
<param name="android-package" value="org.apache.cordova.splashscreen.SplashScreen" />
</feature>
<plugin name="cordova-plugin-console" spec="^1.0.5" />
<plugin name="cordova-plugin-crosswalk-webview" spec="^2.3.0">
<variable name="XWALK_VERSION" value="23+" />
<variable name="XWALK_LITEVERSION" value="xwalk_core_library_canary:17+" />
<variable name="XWALK_COMMANDLINE" value="--disable-pull-to-refresh-effect" />
<variable name="XWALK_MODE" value="embedded" />
<variable name="XWALK_MULTIPLEAPK" value="true" />
</plugin>
<plugin name="cordova-plugin-device" spec="^1.1.4" />
<plugin name="cordova-plugin-splashscreen" spec="^4.0.3" />
<plugin name="cordova-plugin-statusbar" spec="^2.2.3" />
<plugin name="cordova-plugin-whitelist" spec="^1.3.1" />
<plugin name="ionic-plugin-keyboard" spec="^2.2.1" />
<universal-links>
<host name="bhgx5.app.goo.gl/XktS" scheme="https" />
<host name="dynamicbible-7c6cf.firebaseapp.com" scheme="https">
<path url="/__/auth/callback" />
</host>
</universal-links>
<preference name="AndroidLaunchMode" value="singleTask" />
<plugin name="cordova-plugin-console" spec="^1.1.0" />
<plugin name="cordova-plugin-crosswalk-webview" spec="^2.4.0">
<variable name="XWALK_VERSION" value="23+" />
<variable name="XWALK_LITEVERSION" value="xwalk_core_library_canary:17+" />
<variable name="XWALK_COMMANDLINE" value="--disable-pull-to-refresh-effect" />
<variable name="XWALK_MODE" value="embedded" />
<variable name="XWALK_MULTIPLEAPK" value="true" />
</plugin>
<plugin name="cordova-plugin-device" spec="^1.1.7" />
<plugin name="cordova-plugin-splashscreen" spec="^4.1.0" />
<plugin name="cordova-plugin-statusbar" spec="^2.4.1" />
<plugin name="cordova-plugin-whitelist" spec="^1.3.3" />
<plugin name="ionic-plugin-keyboard" spec="^2.2.1" />
<plugin name="cordova-universal-links-plugin" spec="^1.2.1" />
<plugin name="cordova-plugin-buildinfo" spec="^2.0.1" />
<plugin name="cordova-plugin-browsertab" spec="^0.2.0" />
<plugin name="cordova-plugin-inappbrowser" spec="^2.0.1" />
<plugin name="cordova-plugin-inappbrowser" spec="^2.0.2" />
<plugin name="cordova-plugin-customurlscheme" spec="^4.3.0">
<variable name="URL_SCHEME" value="com.firebase.cordova" />
<variable name="ANDROID_SCHEME" value=" " />
<variable name="ANDROID_HOST" value=" " />
<variable name="ANDROID_PATHPREFIX" value="/" />
</plugin>
<preference name="AndroidLaunchMode" value="singleTask" />
<engine name="android" spec="6.2.3" />
</widget>

View File

@ -1,48 +0,0 @@
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var rootdir = process.argv[2];
if (rootdir)
{
console.log("Root: " + rootdir);
// go through each of the platform directories that have been prepared
var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []);
for (var x = 0; x < platforms.length; x++)
{
// // open up the index.html file at the www root
// try
// {
// var platform = platforms[x].trim().toLowerCase();
// var testBuildPath;
// if (platform === 'android')
// {
// testBuildPath = path.join(rootdir, 'platforms', platform, 'app', 'assets', 'www', 'build');
// }
// else
// {
// testBuildPath = path.join(rootdir, 'platforms', platform, 'app', 'www', 'build');
// }
// if (fs.existsSync(testBuildPath))
// {
// console.log('Removing map files from assets after prepare: ' + testBuildPath);
// fs.unlinkSync(testBuildPath + '/vendor.js.map');
// fs.unlinkSync(testBuildPath + '/main.js.map');
// fs.unlinkSync(testBuildPath + '/main.css.map');
// }
// else
// {
// console.log('Build dir @ ' + testBuildPath + ' does not exist for removal');
// }
// } catch (e)
// {
// process.stdout.write(e);
// }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@
"test-coverage": "ng test --code-coverage"
},
"dependencies": {
"@angular/cli": "1.6.3",
"@angular/common": "5.0.0",
"@angular/compiler": "5.0.0",
"@angular/compiler-cli": "5.0.0",
@ -38,10 +37,10 @@
"cordova-plugin-buildinfo": "^2.0.1",
"cordova-plugin-compat": "^1.2.0",
"cordova-plugin-console": "^1.1.0",
"cordova-plugin-crosswalk-webview": "^2.3.0",
"cordova-plugin-crosswalk-webview": "^2.4.0",
"cordova-plugin-customurlscheme": "^4.3.0",
"cordova-plugin-device": "^1.1.7",
"cordova-plugin-inappbrowser": "^2.0.1",
"cordova-plugin-inappbrowser": "^2.0.2",
"cordova-plugin-splashscreen": "^4.1.0",
"cordova-plugin-statusbar": "^2.4.1",
"cordova-plugin-whitelist": "^1.3.3",
@ -56,7 +55,7 @@
"zone.js": "0.8.18"
},
"devDependencies": {
"@angular/cli": "1.1.2",
"@angular/cli": "1.6.3",
"@ionic/app-scripts": "3.1.0",
"@types/jasmine": "2.5.41",
"@types/node": "7.0.4",

View File

@ -237,10 +237,10 @@
"cordova-plugin-browsertab": "0.2.0",
"cordova-plugin-buildinfo": "2.0.1",
"cordova-plugin-console": "1.1.0",
"cordova-plugin-crosswalk-webview": "2.3.0",
"cordova-plugin-crosswalk-webview": "2.4.0",
"cordova-plugin-customurlscheme": "4.3.0",
"cordova-plugin-device": "1.1.7",
"cordova-plugin-inappbrowser": "2.0.1",
"cordova-plugin-inappbrowser": "2.0.2",
"cordova-plugin-splashscreen": "4.1.0",
"cordova-plugin-statusbar": "2.4.1",
"cordova-plugin-whitelist": "1.3.3",

View File

@ -29,7 +29,17 @@ def DEFAULT_MIN_SDK_VERSION = 14
def getConfigPreference(name) {
name = name.toLowerCase()
def xml = file("res/xml/config.xml").getText()
def xml
if (file("src/main/res/xml/config.xml").exists()) {
// cordova-android >= 7.0.0
xml = file("src/main/res/xml/config.xml").getText()
} else {
// cordova-android < 7.0.0
xml = file("res/xml/config.xml").getText()
}
// Disable namespace awareness since Cordova doesn't use them properly
def root = new XmlParser(false, false).parseText(xml)

View File

@ -82,10 +82,10 @@ module.exports.metadata =
"cordova-plugin-browsertab": "0.2.0",
"cordova-plugin-buildinfo": "2.0.1",
"cordova-plugin-console": "1.1.0",
"cordova-plugin-crosswalk-webview": "2.3.0",
"cordova-plugin-crosswalk-webview": "2.4.0",
"cordova-plugin-customurlscheme": "4.3.0",
"cordova-plugin-device": "1.1.7",
"cordova-plugin-inappbrowser": "2.0.1",
"cordova-plugin-inappbrowser": "2.0.2",
"cordova-plugin-splashscreen": "4.1.0",
"cordova-plugin-statusbar": "2.4.1",
"cordova-plugin-whitelist": "1.3.3",

View File

@ -25,6 +25,9 @@ import android.provider.Browser;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -51,6 +54,7 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.Config;
@ -67,6 +71,8 @@ import org.json.JSONObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.StringTokenizer;
@ -91,6 +97,16 @@ public class InAppBrowser extends CordovaPlugin {
private static final String SHOULD_PAUSE = "shouldPauseOnSuspend";
private static final Boolean DEFAULT_HARDWARE_BACK = true;
private static final String USER_WIDE_VIEW_PORT = "useWideViewPort";
private static final String TOOLBAR_COLOR = "toolbarcolor";
private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption";
private static final String CLOSE_BUTTON_COLOR = "closebuttoncolor";
private static final String HIDE_NAVIGATION = "hidenavigationbuttons";
private static final String NAVIGATION_COLOR = "navigationbuttoncolor";
private static final String HIDE_URL = "hideurlbar";
private static final String FOOTER = "footer";
private static final String FOOTER_COLOR = "footercolor";
private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR);
private InAppBrowserDialog dialog;
private WebView inAppWebView;
@ -109,6 +125,14 @@ public class InAppBrowser extends CordovaPlugin {
private ValueCallback<Uri[]> mUploadCallbackLollipop;
private final static int FILECHOOSER_REQUESTCODE = 1;
private final static int FILECHOOSER_REQUESTCODE_LOLLIPOP = 2;
private String closeButtonCaption = "";
private String closeButtonColor = "";
private int toolbarColor = android.graphics.Color.LTGRAY;
private boolean hideNavigationButtons = false;
private String navigationButtonColor = "";
private boolean hideUrlBar = false;
private boolean showFooter = false;
private String footerColor = "";
/**
* Executes the request and returns PluginResult.
@ -127,7 +151,7 @@ public class InAppBrowser extends CordovaPlugin {
t = SELF;
}
final String target = t;
final HashMap<String, Boolean> features = parseFeature(args.optString(2));
final HashMap<String, String> features = parseFeature(args.optString(2));
LOG.d(LOG_TAG, "target = " + target);
@ -366,18 +390,21 @@ public class InAppBrowser extends CordovaPlugin {
* @param optString
* @return
*/
private HashMap<String, Boolean> parseFeature(String optString) {
private HashMap<String, String> parseFeature(String optString) {
if (optString.equals(NULL)) {
return null;
} else {
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
HashMap<String, String> map = new HashMap<String, String>();
StringTokenizer features = new StringTokenizer(optString, ",");
StringTokenizer option;
while(features.hasMoreElements()) {
option = new StringTokenizer(features.nextToken(), "=");
if (option.hasMoreElements()) {
String key = option.nextToken();
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
String value = option.nextToken();
if (!customizableOptions.contains(key)){
value = value.equals("yes") || value.equals("no") ? value : "yes";
}
map.put(key, value);
}
}
@ -406,7 +433,7 @@ public class InAppBrowser extends CordovaPlugin {
intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName());
this.cordova.getActivity().startActivity(intent);
return "";
// not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
// not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
} catch (java.lang.RuntimeException e) {
LOG.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
return e.toString();
@ -523,7 +550,7 @@ public class InAppBrowser extends CordovaPlugin {
* @param url the url to load.
* @param features jsonObject
*/
public String showWebPage(final String url, HashMap<String, Boolean> features) {
public String showWebPage(final String url, HashMap<String, String> features) {
// Determine if we should hide the location bar.
showLocationBar = true;
showZoomControls = true;
@ -531,44 +558,74 @@ public class InAppBrowser extends CordovaPlugin {
mediaPlaybackRequiresUserGesture = false;
if (features != null) {
Boolean show = features.get(LOCATION);
String show = features.get(LOCATION);
if (show != null) {
showLocationBar = show.booleanValue();
showLocationBar = show.equals("yes") ? true : false;
}
Boolean zoom = features.get(ZOOM);
if(showLocationBar) {
String hideNavigation = features.get(HIDE_NAVIGATION);
String hideUrl = features.get(HIDE_URL);
if(hideNavigation != null) hideNavigationButtons = hideNavigation.equals("yes") ? true : false;
if(hideUrl != null) hideUrlBar = hideUrl.equals("yes") ? true : false;
}
String zoom = features.get(ZOOM);
if (zoom != null) {
showZoomControls = zoom.booleanValue();
showZoomControls = zoom.equals("yes") ? true : false;
}
Boolean hidden = features.get(HIDDEN);
String hidden = features.get(HIDDEN);
if (hidden != null) {
openWindowHidden = hidden.booleanValue();
openWindowHidden = hidden.equals("yes") ? true : false;
}
Boolean hardwareBack = features.get(HARDWARE_BACK_BUTTON);
String hardwareBack = features.get(HARDWARE_BACK_BUTTON);
if (hardwareBack != null) {
hadwareBackButton = hardwareBack.booleanValue();
hadwareBackButton = hardwareBack.equals("yes") ? true : false;
} else {
hadwareBackButton = DEFAULT_HARDWARE_BACK;
}
Boolean mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION);
String mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION);
if (mediaPlayback != null) {
mediaPlaybackRequiresUserGesture = mediaPlayback.booleanValue();
mediaPlaybackRequiresUserGesture = mediaPlayback.equals("yes") ? true : false;
}
Boolean cache = features.get(CLEAR_ALL_CACHE);
String cache = features.get(CLEAR_ALL_CACHE);
if (cache != null) {
clearAllCache = cache.booleanValue();
clearAllCache = cache.equals("yes") ? true : false;
} else {
cache = features.get(CLEAR_SESSION_CACHE);
if (cache != null) {
clearSessionCache = cache.booleanValue();
clearSessionCache = cache.equals("yes") ? true : false;
}
}
Boolean shouldPause = features.get(SHOULD_PAUSE);
String shouldPause = features.get(SHOULD_PAUSE);
if (shouldPause != null) {
shouldPauseInAppBrowser = shouldPause.booleanValue();
shouldPauseInAppBrowser = shouldPause.equals("yes") ? true : false;
}
Boolean wideViewPort = features.get(USER_WIDE_VIEW_PORT);
String wideViewPort = features.get(USER_WIDE_VIEW_PORT);
if (wideViewPort != null ) {
useWideViewPort = wideViewPort.booleanValue();
useWideViewPort = wideViewPort.equals("yes") ? true : false;
}
String closeButtonCaptionSet = features.get(CLOSE_BUTTON_CAPTION);
if (closeButtonCaptionSet != null) {
closeButtonCaption = closeButtonCaptionSet;
}
String closeButtonColorSet = features.get(CLOSE_BUTTON_COLOR);
if (closeButtonColorSet != null) {
closeButtonColor = closeButtonColorSet;
}
String toolbarColorSet = features.get(TOOLBAR_COLOR);
if (toolbarColorSet != null) {
toolbarColor = android.graphics.Color.parseColor(toolbarColorSet);
}
String navigationButtonColorSet = features.get(NAVIGATION_COLOR);
if (navigationButtonColorSet != null) {
navigationButtonColor = navigationButtonColorSet;
}
String showFooterSet = features.get(FOOTER);
if (showFooterSet != null) {
showFooter = showFooterSet.equals("yes") ? true : false;
}
String footerColorSet = features.get(FOOTER_COLOR);
if (footerColorSet != null) {
footerColor = footerColorSet;
}
}
@ -583,13 +640,60 @@ public class InAppBrowser extends CordovaPlugin {
*/
private int dpToPixels(int dipValue) {
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
(float) dipValue,
cordova.getActivity().getResources().getDisplayMetrics()
(float) dipValue,
cordova.getActivity().getResources().getDisplayMetrics()
);
return value;
}
private View createCloseButton(int id){
View _close;
Resources activityRes = cordova.getActivity().getResources();
if (closeButtonCaption != "") {
// Use TextView for text
TextView close = new TextView(cordova.getActivity());
close.setText(closeButtonCaption);
close.setTextSize(20);
if (closeButtonColor != "") close.setTextColor(android.graphics.Color.parseColor(closeButtonColor));
close.setGravity(android.view.Gravity.CENTER_VERTICAL);
close.setPadding(this.dpToPixels(10), 0, this.dpToPixels(10), 0);
_close = close;
}
else {
ImageButton close = new ImageButton(cordova.getActivity());
int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName());
Drawable closeIcon = activityRes.getDrawable(closeResId);
if (closeButtonColor != "") close.setColorFilter(android.graphics.Color.parseColor(closeButtonColor));
close.setImageDrawable(closeIcon);
close.setScaleType(ImageView.ScaleType.FIT_CENTER);
if (Build.VERSION.SDK_INT >= 16)
close.getAdjustViewBounds();
_close = close;
}
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
_close.setLayoutParams(closeLayoutParams);
if (Build.VERSION.SDK_INT >= 16)
_close.setBackground(null);
else
_close.setBackgroundDrawable(null);
_close.setContentDescription("Close Button");
_close.setId(Integer.valueOf(id));
_close.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeDialog();
}
});
return _close;
}
@SuppressLint("NewApi")
public void run() {
@ -612,7 +716,7 @@ public class InAppBrowser extends CordovaPlugin {
// Toolbar layout
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
//Please, no more black!
toolbar.setBackgroundColor(android.graphics.Color.LTGRAY);
toolbar.setBackgroundColor(toolbarColor);
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
toolbar.setHorizontalGravity(Gravity.LEFT);
@ -635,6 +739,7 @@ public class InAppBrowser extends CordovaPlugin {
Resources activityRes = cordova.getActivity().getResources();
int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName());
Drawable backIcon = activityRes.getDrawable(backResId);
if (navigationButtonColor != "") back.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor));
if (Build.VERSION.SDK_INT >= 16)
back.setBackground(null);
else
@ -660,6 +765,7 @@ public class InAppBrowser extends CordovaPlugin {
forward.setId(Integer.valueOf(3));
int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName());
Drawable fwdIcon = activityRes.getDrawable(fwdResId);
if (navigationButtonColor != "") forward.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor));
if (Build.VERSION.SDK_INT >= 16)
forward.setBackground(null);
else
@ -692,37 +798,37 @@ public class InAppBrowser extends CordovaPlugin {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
navigate(edittext.getText().toString());
return true;
navigate(edittext.getText().toString());
return true;
}
return false;
}
});
// Close/Done button
ImageButton close = new ImageButton(cordova.getActivity());
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
close.setLayoutParams(closeLayoutParams);
close.setContentDescription("Close Button");
close.setId(Integer.valueOf(5));
int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName());
Drawable closeIcon = activityRes.getDrawable(closeResId);
if (Build.VERSION.SDK_INT >= 16)
close.setBackground(null);
else
close.setBackgroundDrawable(null);
close.setImageDrawable(closeIcon);
close.setScaleType(ImageView.ScaleType.FIT_CENTER);
back.setPadding(0, this.dpToPixels(10), 0, this.dpToPixels(10));
if (Build.VERSION.SDK_INT >= 16)
close.getAdjustViewBounds();
close.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeDialog();
}
});
// Header Close/Done button
View close = createCloseButton(5);
toolbar.addView(close);
// Footer
RelativeLayout footer = new RelativeLayout(cordova.getActivity());
int _footerColor;
if(footerColor != ""){
_footerColor = Color.parseColor(footerColor);
}else{
_footerColor = android.graphics.Color.LTGRAY;
}
footer.setBackgroundColor(_footerColor);
RelativeLayout.LayoutParams footerLayout = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44));
footerLayout.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
footer.setLayoutParams(footerLayout);
if (closeButtonCaption != "") footer.setPadding(this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8));
footer.setHorizontalGravity(Gravity.LEFT);
footer.setVerticalGravity(Gravity.BOTTOM);
View footerClose = createCloseButton(7);
footer.addView(footerClose);
// WebView
inAppWebView = new WebView(cordova.getActivity());
@ -825,10 +931,9 @@ public class InAppBrowser extends CordovaPlugin {
actionButtonContainer.addView(back);
actionButtonContainer.addView(forward);
// Add the views to our toolbar
toolbar.addView(actionButtonContainer);
toolbar.addView(edittext);
toolbar.addView(close);
// Add the views to our toolbar if they haven't been disabled
if (!hideNavigationButtons) toolbar.addView(actionButtonContainer);
if (!hideUrlBar) toolbar.addView(edittext);
// Don't add the toolbar if its been disabled
if (getShowLocationBar()) {
@ -837,7 +942,14 @@ public class InAppBrowser extends CordovaPlugin {
}
// Add our webview to our main view/layout
main.addView(inAppWebView);
RelativeLayout webViewLayout = new RelativeLayout(cordova.getActivity());
webViewLayout.addView(inAppWebView);
main.addView(webViewLayout);
// Don't add the footer unless it's been enabled
if (showFooter) {
webViewLayout.addView(footer);
}
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
@ -1027,7 +1139,7 @@ public class InAppBrowser extends CordovaPlugin {
// Update the UI if we haven't already
if (!newloc.equals(edittext.getText().toString())) {
edittext.setText(newloc);
}
}
try {
JSONObject obj = new JSONObject();

View File

@ -6,22 +6,33 @@
</ion-item>
<ion-card-content *ngIf="data !== undefined && data.status === 0">
<br>
<div class="passage-text" *ngFor="let ch of data.cs">
<ng-template [ngIf]="this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of withParas">
<h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b>
</h2>
<div *ngFor="let para of getParas(ch)">
<div *ngFor="let para of ch.paras">
<h3 *ngIf="hasHeader(para.p)">{{para.p.h}}</h3>
<p>
<span *ngFor="let vs of para.vss">
<b *ngIf="showVerseNumbers">{{vs.v}}.</b> <span *ngFor="let w of vs.w">
<ng-template ngFor let-vs [ngForOf]="para.vss">
<b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</span><br *ngIf="versesOnNewLine">
</span>
</ng-template><br *ngIf="this.profileService.profile().verses_on_new_line">
</ng-template>
</p>
</div>
</div>
</div></ng-template>
<ng-template [ngIf]="!this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of data.cs">
<h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b>
</h2>
<ng-template ngFor let-vs [ngForOf]="ch.vss">
<b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</ng-template><br *ngIf="this.profileService.profile().verses_on_new_line">
</ng-template>
</div></ng-template>
</ion-card-content>
<ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message>

View File

@ -19,13 +19,8 @@ export class Passage implements OnInit
@Input()
cardItem: CardItem;
@Input()
versesOnNewLine: boolean;
@Input()
showVerseNumbers: boolean;
data: BiblePassageResult;
withParas: BibleParaPassage[];
ref: Reference;
constructor(private bibleService: BibleService, private elementRef: ElementRef, private profileService: ProfileService)
@ -35,7 +30,17 @@ export class Passage implements OnInit
ngOnInit(): void
{
this.ref = new Reference(this.cardItem.qry);
this.bibleService.getResultAsPromise(this.ref.Section).then(data => this.data = data);
this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{
this.setData(data);
});
}
setData(data: BiblePassageResult)
{
this.data = data;
this.withParas = this.getParaPassages(data.cs);
}
close()
@ -70,7 +75,7 @@ export class Passage implements OnInit
this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{
this.data = data;
this.setData(data);
this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref);
});
@ -89,7 +94,7 @@ export class Passage implements OnInit
this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{
this.data = data;
this.setData(data);
this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref);
});
@ -136,7 +141,7 @@ export class Passage implements OnInit
this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{
this.data = data;
this.setData(data);
this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref);
});
@ -174,22 +179,29 @@ export class Passage implements OnInit
return this.ref.Section.start.book.book_number + ';' + this.ref.Section.start.chapter + ';' + vs.v;
}
getParaPassages(chapters: BiblePassage[])
{
let passages: BibleParaPassage[] = [];
for (let ch of chapters)
{
let para = {
ch: ch.ch,
paras: this.getParas(ch)
};
passages.push(para);
}
return passages;
}
getParas(ch: BiblePassage)
{
// group the verses into paragraphs.
// create an initial paragraph to hold verses that might come before a paragraph.
let para: BiblePara = { p: { h: '', p: 0 }, vss: [] };
let paras: BiblePara[] = [];
// if you aren't showing paragraphs, stick em all in the same paragraph.
if (!this.profileService.profile().show_paragraphs)
{
para.vss = ch.vss;
paras.push(para);
return paras;
}
// for each verse in the chapter, break them into paragraphs.
for (let v of ch.vss)
{
@ -218,3 +230,8 @@ type BiblePara = {
p: Paragraph,
vss: BibleVerse[],
}
type BibleParaPassage = {
ch: number;
paras: BiblePara[];
}

View File

@ -1,4 +1,5 @@
verse-picker {
.button {
color: #fff;
font-size: 1em;
@ -7,7 +8,7 @@ verse-picker {
background-color: #1c2e4c;
margin: .3em;
text-align: center;
width: 75px;
width: 95px;
}
.backbutton {

View File

@ -22,35 +22,35 @@
<ion-list-header>Search Settings</ion-list-header>
<ion-item>
<ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().strongs_modal" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().strongs_modal" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Clear Search after Query</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().clear_search_after_query" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().clear_search_after_query" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Append Results Below</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().append_to_bottom" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().append_to_bottom" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().insert_next_to_item" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().insert_next_to_item" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Each Verse on New Line</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().verses_on_new_line" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().verses_on_new_line" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Verse #'s</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraphs</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="save()"></ion-toggle>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-list-header>Adjust Text</ion-list-header>
@ -97,8 +97,7 @@
<ion-content #searchcontent padding class="search-card">
<ion-card *ngFor="let item of this.profileService.profile().items">
<passage *ngIf="isPassage(item.type)" [cardItem]="item" [versesOnNewLine]="this.profileService.profile().verses_on_new_line"
[showVerseNumbers]="this.profileService.profile().show_verse_numbers" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></passage>
<passage *ngIf="isPassage(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></passage>
<strongs *ngIf="isStrongs(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></strongs>
<words *ngIf="isWords(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></words>
<error *ngIf="isError(item.type)" [cardItem]="item" (onClose)="removeItem($event)"></error>

View File

@ -12,8 +12,7 @@ import { VersePickerModal } from '../../components/verse-picker/verse-picker';
@Component({
templateUrl: 'search.html'
})
export class SearchPage implements OnInit
{
export class SearchPage implements OnInit {
searchQuery = '';
last: CardItem;
loader: Loading;
@ -27,34 +26,43 @@ export class SearchPage implements OnInit
, public modalCtrl: ModalController
, public profileService: ProfileService
, public params: NavParams
)
{
) {
}
ngOnInit(): void
{
let t = this.profileService.profile();
// Check if there is a profile saved in local storage
this.loader = this.loadingCtrl.create({ content: 'Loading Page...' });
this.loader.present().then(() =>
{
this.initializeItems(t);
ngOnInit(): void {
if (this.profileService.localIsLoaded) {
this.loader = this.loadingCtrl.create({ content: 'Loading Page...' });
this.loader.present().then(() => {
let t = this.profileService.profile();
this.initializeItems(t);
this.loader.dismiss();
});
}
else
{
this.profileService.onLocalStorageLoaded.subscribe(t => {
// Check if there is a profile saved in local storage
this.loader = this.loadingCtrl.create({ content: 'Loading Page...' });
this.loader.present().then(() => {
this.initializeItems(t);
this.loader.dismiss();
});
});
this.profileService.onSavedPagesChanged.subscribe(sp => {
this.pagesService.initializePages(sp);
});
}
}
initializeItems(u: User)
{
initializeItems(u: User) {
// migrate old way of storing card items to the new.
let has_migrated = false;
for (let i in u.items)
{
if (u.items.hasOwnProperty(i))
{
for (let i in u.items) {
if (u.items.hasOwnProperty(i)) {
let ci = u.items[i];
if (ci['data'] !== undefined)
{
if (ci['data'] !== undefined) {
if (ci['data'].qry !== undefined)
u.items[i] = { qry: ci['data'].qry, dict: ci.dict, type: ci.type };
else if (ci['data'].ref !== undefined)
@ -73,15 +81,11 @@ export class SearchPage implements OnInit
}
}
for (let pg of u.saved_pages)
{
for (let i in pg.queries)
{
if (pg.queries.hasOwnProperty(i))
{
for (let pg of u.saved_pages) {
for (let i in pg.queries) {
if (pg.queries.hasOwnProperty(i)) {
let ci = pg.queries[i];
if (ci['data'] !== undefined)
{
if (ci['data'] !== undefined) {
if (ci['data'].qry !== undefined)
pg.queries[i] = { qry: ci['data'].qry, dict: ci.dict, type: ci.type };
else if (ci['data'].ref !== undefined)
@ -105,7 +109,7 @@ export class SearchPage implements OnInit
this.pagesService.initializePages(u.saved_pages);
if (this.params.data.queries !== undefined)
this.profileService.profile().items = this.params.data.queries.slice();
this.profileService.profile().items = JSON.parse(JSON.stringify(this.params.data.queries));
if (this.params.data.title === undefined)
this.title = 'Search';
@ -114,25 +118,19 @@ export class SearchPage implements OnInit
if (has_migrated)
this.profileService.save();
}
save()
{
this.profileService.save();
}
textSizeChanged()
{
textSizeChanged() {
this.profileService.textSizeChanged();
this.save();
this.profileService.localSave();
}
actionsMenu()
{
actionsMenu() {
this.menu.open('actions');
}
addPage()
{
addPage() {
const alert = this.alertCtrl.create({
title: 'Save Search as Page',
inputs: [
@ -145,15 +143,13 @@ export class SearchPage implements OnInit
{
text: 'Cancel',
role: 'cancel',
handler: (): void =>
{
handler: (): void => {
console.log('Cancel clicked');
}
},
{
text: 'Save',
handler: data =>
{
handler: data => {
const p = { queries: this.profileService.profile().items.slice(), title: data.title };
this.profileService.profile().saved_pages.push(p);
this.profileService.save();
@ -165,71 +161,58 @@ export class SearchPage implements OnInit
alert.present();
}
updatePage()
{
updatePage() {
const page = this.profileService.profile().saved_pages.find(
i =>
i.title === this.params.data.title
i.title === this.params.data.title
);
page.queries = this.profileService.profile().items.slice();
this.profileService.save();
}
setQuery(searchbar)
{
setQuery(searchbar) {
this.searchQuery = searchbar.target.value;
}
getQuery(searchbar)
{
getQuery(searchbar) {
this.updateUIwithItems(this.searchQuery, true);
}
isError(t: string)
{
isError(t: string) {
return t === 'Error';
}
isPassage(t: string)
{
isPassage(t: string) {
return t === 'Passage';
}
isStrongs(t: string)
{
isStrongs(t: string) {
return t === 'Strongs';
}
isWords(t: string)
{
isWords(t: string) {
return t === 'Words';
}
versePicker()
{
versePicker() {
const modal = this.modalCtrl.create(VersePickerModal, { onItemClicked: this });
modal.present();
}
removeItem(item)
{
removeItem(item) {
const idx = this.profileService.profile().items.indexOf(item);
this.profileService.profile().items.splice(idx, 1);
// save the users settings.
this.profileService.save();
this.profileService.localSave();
}
addItemToList(item: CardItem)
{
if (this.profileService.profile().append_to_bottom)
{
if (this.last != null && this.profileService.profile().insert_next_to_item)
{
addItemToList(item: CardItem) {
if (this.profileService.profile().append_to_bottom) {
if (this.last != null && this.profileService.profile().insert_next_to_item) {
const idx = this.profileService.profile().items.indexOf(this.last);
this.profileService.profile().items.splice(idx + 1, 0, item);
} else
this.profileService.profile().items.push(item);
}
else
{
if (this.last != null && this.profileService.profile().insert_next_to_item)
{
else {
if (this.last != null && this.profileService.profile().insert_next_to_item) {
const idx = this.profileService.profile().items.indexOf(this.last);
this.profileService.profile().items.splice(idx, 0, item);
} else
@ -237,33 +220,25 @@ export class SearchPage implements OnInit
}
this.last = null;
}
getItemsNextToCard(data: OpenData)
{
getItemsNextToCard(data: OpenData) {
this.last = data.card;
this.updateUIwithItems(data.qry, data.from_search_bar);
}
getItemList(search: string): Promise<CardItem[]>
{
return new Promise((resolve) =>
{
getItemList(search: string): Promise<CardItem[]> {
return new Promise((resolve) => {
const list: CardItem[] = [];
try
{
try {
const qs = search.split(';');
for (let x in qs)
{
if (qs.hasOwnProperty(x))
{
for (let x in qs) {
if (qs.hasOwnProperty(x)) {
let q = qs[x].trim();
if (q !== '')
{
if (q !== '') {
// its a search term.
if (q.search(/[0-9]/i) === -1)
list.push({ qry: q, dict: 'na', type: 'Words' });
else if (q.search(/(H|G)[0-9]/i) !== -1)
{
else if (q.search(/(H|G)[0-9]/i) !== -1) {
// its a strongs lookup
let dict = q.substring(0, 1);
@ -275,11 +250,9 @@ export class SearchPage implements OnInit
q = q.substring(1, q.length);
list.push({ qry: q, dict: dict, type: 'Strongs' });
}
else
{
else {
// its a verse reference.
if (q.trim() !== '')
{
if (q.trim() !== '') {
const myref = new Reference(q.trim());
list.push({ qry: myref.toString(), dict: myref.Section.start.book.book_number > 39 ? 'G' : 'H', type: 'Passage' });
}
@ -292,8 +265,7 @@ export class SearchPage implements OnInit
this.profileService.save();
}
catch (error)
{
catch (error) {
list.push({ qry: error, type: 'Error', dict: 'na' });
console.log(error);
}
@ -302,18 +274,13 @@ export class SearchPage implements OnInit
});
}
updateUIwithItems(search: string, from_search_bar: boolean)
{
this.getItemList(search).then(lst =>
{
updateUIwithItems(search: string, from_search_bar: boolean) {
this.getItemList(search).then(lst => {
this.loader = this.loadingCtrl.create({ content: 'Looking up Query...' });
this.loader.present().then(
() =>
{
for (let item of lst)
{
if (item.type === 'Strongs' && this.profileService.profile().strongs_modal && !from_search_bar)
{
() => {
for (let item of lst) {
if (item.type === 'Strongs' && this.profileService.profile().strongs_modal && !from_search_bar) {
const modal = this.modalCtrl.create(StrongsModal, { sn: parseInt(item.qry), dict: item.dict, onItemClicked: this });
modal.present();
} else
@ -330,8 +297,7 @@ export type OpenData = { card: CardItem, qry: string, from_search_bar: boolean }
export type CardItem = { qry: string, type: string, dict: string }
class Item
{
class Item {
id: number;
data: any;
type: Type<any>;

View File

@ -9,7 +9,7 @@
<ion-content padding>
<ng-template [ngIf]="profileService.profile()">
<h4>Search Settings</h4>
{{profileService.isWeb}} : {{profileService.url}}
<ng-template [ngIf]="!profileService.currentUser()">
<ion-item>
<button ion-button (click)="profileService.authenticate()">Login With Google</button>

View File

@ -20,12 +20,7 @@ export class SettingsPage
textSizeChanged()
{
this.profileService.textSizeChanged();
this.save();
}
save()
{
this.profileService.save()
this.profileService.localSave();
}
reset()

View File

@ -11,45 +11,36 @@ import { CardItem } from '../pages/search/search';
import { Promise } from 'q';
import { setTimeout } from 'timers';
type fbObject<T> = {
ref: AngularFireObject<T>,
stream: Observable<T>,
};
import { Output, EventEmitter } from '@angular/core';
export const DEFAULT_USER_NAME = 'john_doe';
export type User = {
username: string,
uid: string | null,
strongs_modal: boolean,
clear_search_after_query: boolean,
items: CardItem[],
append_to_bottom: boolean,
insert_next_to_item: boolean,
font_size: number,
saved_pages: SavedPage[],
verses_on_new_line: boolean,
show_verse_numbers: boolean,
show_paragraphs: boolean,
show_paragraph_headings: boolean,
}
export type SavedPage = {
queries: CardItem[],
title: string,
}
@Injectable()
export class ProfileService
{
@Output()
onSavedPagesChanged = new EventEmitter<SavedPage[]>();
@Output()
onLocalStorageLoaded = new EventEmitter<User>();
localProfile: User;
remoteProfile: fbObject<User> | null;
remoteLoggedIn: boolean;
isWeb: boolean;
url: string;
needsSync = false;
firebaseUser: firebase.User;
remoteLoggedIn: boolean;
localIsLoaded: boolean;
constructor(private local: Storage, private db: AngularFireDatabase, public firebaseAuth: AngularFireAuth)
constructor(
private local: Storage,
private db: AngularFireDatabase,
public firebaseAuth: AngularFireAuth
)
{
this.isWeb = (document.URL.startsWith('http') || !document.URL.startsWith('http://localhost:8080'));
this.url = document.URL;
this.isWeb = (document.URL.startsWith('http') && !document.URL.startsWith('http://localhost:8080'));
this.localIsLoaded = false;
// asyncrounosly kick off a poller that does the work of syncing remotely when the
// profile needs to be synced.
@ -66,36 +57,29 @@ export class ProfileService
{
let st = new Date(); console.log('Saving the remote profile...');
self.remoteProfile.ref.set(self.localProfile);
console.log('Finished saving remote profile. ' + self.elapsed(st, new Date) + 'ms');
console.log(' Finished saving remote profile. ' + self.elapsed(st, new Date) + 'ms');
}
self.needsSync = false;
}
poll(self);
}, 3000);
}, 10000);
})(this);
let localObserver = this.userObserver().subscribe(
user =>
{
this.update(user);
this.localProfile = user;
},
error => console.log(error)
);
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
}
userObserver(): Observable<User>
{
return Observable.fromPromise(this.local.get('profile')).map(json_profile =>
this.local.get('profile').then(json_profile =>
{
let t = this.profile();
if (json_profile !== null) t = JSON.parse(json_profile);
return t;
if (json_profile !== null)
t = JSON.parse(json_profile);
this.localProfile = t;
this.localIsLoaded = true;
this.onLocalStorageLoaded.emit(this.localProfile);
});
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
}
profile(): User
@ -105,50 +89,141 @@ export class ProfileService
this.localProfile = ProfileService.createDefaultUser();
}
return this.localProfile
return this.localProfile;
}
subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User)
{
if (!user) return;
console.log('subscribeToRemoteProfile');
if (!user || this.firebaseUser) return;
console.log('You got the firebase user.');
let obj = db.object('/settings/' + user.uid);
this.remoteProfile = {
ref: obj as AngularFireObject<User>,
stream: obj.valueChanges() as Observable<User>,
};
this.firebaseUser = user;
this.profile().username = user.displayName;
this.profile().uid = user.uid;
this.save();
this.remoteProfile.stream.subscribe(
user => this.handleRemotePreferenceChange(user),
error => console.log(error));
}
comparePage(a: SavedPage, b: SavedPage)
{
if (a.title > b.title)
return 1;
if (a.title === b.title)
return 0;
if (a.title < b.title)
return -1;
}
private returnYonly(a: SavedPage[], b: SavedPage[])
{
let r: SavedPage[] = [];
if (b === undefined)
return r;
if (a !== undefined && a.length === 0 && b !== undefined && b.length > 0)
return [...b];
let x = [...a];
let y = [...b];
/// <summary>
/// Takes two javascript arrays and returns an array
/// containing a set of values shared by arrays.
/// </summary>
// declare iterator
let i = 0;
// declare terminator
let t = (x.length < y.length) ? x.length : y.length;
// sort the arrays
x.sort(this.comparePage);
y.sort(this.comparePage);
// in this loop, we remove from the arrays, the
// values that aren't shared between them.
while (i < t)
{
if (x[i].title === y[i].title)
{
i++;
}
if (x.length > i && y.length > i && x[i].title < y[i].title)
x.splice(i, 1);
if (x.length > i && y.length > i && x[i].title > y[i].title)
{
r.unshift(y[i]);
y.splice(i, 1);
}
t = (x.length < y.length) ? x.length : y.length;
if (t === i && t < y.length)
{
r = r.concat(y);
}
}
// we could return y, because at this time, both arrays
// are identical.
return r;
}
handleRemotePreferenceChange(user: User)
{
console.log('handleRemotePreferenceChange');
if (user)
{
if (!user.saved_pages) user.saved_pages = [];
if (!user.items) user.items = [];
let changed = false;
let local_was_empty = this.profile().saved_pages.length === 0;
// merge the saved pages so you don't loose those either
if (this.profile().saved_pages.length > 0)
user.saved_pages.concat(this.localProfile.saved_pages);
// don't add if they are the same.
let ys = this.returnYonly(this.profile().saved_pages, user.saved_pages);
if (ys.length > 0)
{
this.localProfile.saved_pages = this.localProfile.saved_pages.concat(ys);
this.onSavedPagesChanged.emit(this.localProfile.saved_pages);
if (!local_was_empty)
{
changed = true;
this.needsSync = true;
}
}
if (this.profile().saved_pages.length > 0 && user.saved_pages === undefined)
{
changed = true;
this.needsSync = true;
}
// don't sync things that don't make sense.
this.profile().uid = user.uid;
this.profile().username = user.username;
this.profile().saved_pages = user.saved_pages;
if (this.profile().uid !== user.uid)
{
this.profile().uid = user.uid;
changed = true;
}
if (this.profile().username !== user.username)
{
this.profile().username = user.username;
changed = true;
}
// We only save the local change here since this is an update from our remote profile.
this.localSave();
if (changed)
{
this.localSave();
}
}
else
{
// No user is there so we should save our local to the remote.
this.save();
}
}
@ -160,6 +235,8 @@ export class ProfileService
authenticate()
{
console.log('Authenticating to remote...');
let self = this;
let provider = new firebase.auth.GoogleAuthProvider();
if (this.isWeb)
@ -184,12 +261,14 @@ export class ProfileService
refresh()
{
console.log('refresh');
this.logout();
this.authenticate();
}
logout()
{
console.log('logout');
this.firebaseAuth.auth.signOut(); // sign out
this.remoteProfile = null; // inform the profile service not to bother
this.remoteLoggedIn = false;
@ -201,8 +280,9 @@ export class ProfileService
this.needsSync = true;
}
private localSave()
localSave()
{
console.log('saving local');
this.local.set('profile', JSON.stringify(this.profile()));
}
@ -267,6 +347,7 @@ export class ProfileService
{
let idx = this.profile().saved_pages.indexOf(page);
this.profile().saved_pages.splice(idx, 1);
this.onSavedPagesChanged.emit(this.localProfile.saved_pages);
// Sync with remote
this.save();
@ -299,4 +380,31 @@ export class ProfileService
};
}
}
}
type fbObject<T> = {
ref: AngularFireObject<T>,
stream: Observable<T>,
};
export type User = {
username: string,
uid: string | null,
strongs_modal: boolean,
clear_search_after_query: boolean,
items: CardItem[],
append_to_bottom: boolean,
insert_next_to_item: boolean,
font_size: number,
saved_pages: SavedPage[],
verses_on_new_line: boolean,
show_verse_numbers: boolean,
show_paragraphs: boolean,
show_paragraph_headings: boolean,
}
export type SavedPage = {
queries: CardItem[],
title: string,
}