Commit 63a9b904 authored by laurent l's avatar laurent l

IOS: OAuth

parent 391273a2
......@@ -3,20 +3,21 @@ import { Controller } from './controller';
import { BarcodeScanner } from 'nativescript-barcodescanner';
import { AccountsController } from '../controllers/accounts';
import { Database,
ItemOperation,
Item,
Loan,
Account,
Hold,
Portal,
Novelties,
OAuthTokenNotSet,
WrongLoginPassword,
Cookies,
Manager,
Iso8601Date
} from '../models/';
import {
Database,
ItemOperation,
Item,
Loan,
Account,
Hold,
Portal,
Novelties,
OAuthTokenNotSet,
WrongLoginPassword,
Cookies,
Manager,
Iso8601Date
} from '../models/';
import { RadSideDrawer } from 'nativescript-ui-sidedrawer';
import { WebView } from 'ui/web-view';
......@@ -42,31 +43,31 @@ export class LibraryController extends Controller {
public constructor(account: Account, manager: Manager) {
super();
this._account = account;
this._account = account;
this._portal = new Portal();
this._manager = manager;
this._loadLoansAndHolds();
this.set('loans', this._loans);
this.set('holds', this._holds);
this.set('page_title', this._account.label);
this.set('show_scan_item', this._portal.canSearch(this._account));
this.set('show_novelties', this._portal.canFetchNovelties(this._account));
this.set('busy', 'off');
this.set('card', this._account.getCard());
this.set('sip_loan', false);
this._manager
.connect(Database.current(), this._account)
.then( (portal) => {
.then((portal) => {
this.set('sip_loan', portal.sip_loan_feature)
});
}
public sendNotification(message:string) {
public sendNotification(message: string) {
LocalNotifications.requestPermission();
LocalNotifications.schedule([{
......@@ -77,22 +78,28 @@ export class LibraryController extends Controller {
public goHomeAction() {
this.navigate({ moduleName: "views/accounts/list",
bindingContext: new AccountsController(this._manager),
clearHistory: true });
this.navigate({
moduleName: "views/accounts/list",
bindingContext: new AccountsController(this._manager),
clearHistory: true
});
}
public showCardAction() {
let ZXing = require('nativescript-zxing');
let card_img = (new ZXing()).createBarcode({encode: this._account.getCardNumber().toUpperCase(),
width: 1024,
height: 200,
format: ZXing.CODE_39});
let card_img = (new ZXing()).createBarcode({
encode: this._account.getCardNumber().toUpperCase(),
width: 1024,
height: 200,
format: ZXing.CODE_39
});
this.set('card_img', imageSource.fromNativeSource(card_img));
this.set('card_number', this._account.getCard().getNumber());
this.navigate({ moduleName: "views/library/card",
bindingContext: this });
this.navigate({
moduleName: "views/library/card",
bindingContext: this
});
}
......@@ -102,29 +109,33 @@ export class LibraryController extends Controller {
novelties.loadNext();
this.set('novelties', novelties);
}
this.navigate({ moduleName: "views/library/novelties",
bindingContext: this });
this.navigate({
moduleName: "views/library/novelties",
bindingContext: this
});
}
public onLoadMoreNovelties(args) {
this.get('novelties')
.loadNext()
.then( () => {
.then(() => {
args.object.notifyLoadOnDemandFinished();
});
}
public editAccountAction() {
new AccountsController(this._manager).editAccount(this._account);
}
public openLoansAndHoldsAction() {
this.navigate({ moduleName: "views/library/items",
bindingContext: this });
this.navigate({
moduleName: "views/library/items",
bindingContext: this
});
}
......@@ -139,10 +150,10 @@ export class LibraryController extends Controller {
orientation: "portrait",
openSettingsIfPermissionWasPreviouslyDenied: true
})
.then( (result) => {
.then((result) => {
return this._portal.searchUrl(this._account, result.text);
})
.then( (search_url) => {
.then((search_url) => {
this._openWebsite(search_url);
});
}
......@@ -150,7 +161,7 @@ export class LibraryController extends Controller {
public loanItemAction() {
this._closeDrawer();
(new BarcodeScanner())
.scan({
formats: "CODE_39, CODABAR",
......@@ -163,23 +174,25 @@ export class LibraryController extends Controller {
openSettingsIfPermissionWasPreviouslyDenied: true
})
.then(
(result) => {
return result.text;
})
.then( (barcode) => {
(result) => {
return result.text;
})
.then((barcode) => {
this._turnBusyIndicator('on');
return this._portal.fetchItem(this._account, barcode);
})
.then( (item: Item) => {
.then((item: Item) => {
this._turnBusyIndicator('off');
this.set('item', item);
this.navigate({ moduleName: "views/library/loan_item",
backstackVisible: false,
bindingContext: this });
this.navigate({
moduleName: "views/library/loan_item",
backstackVisible: false,
bindingContext: this
});
});
}
public confirmLoanItemAction() {
this.dialogs
.prompt({
......@@ -189,28 +202,32 @@ export class LibraryController extends Controller {
cancelButtonText: L('cancel'),
inputType: this.dialogs.inputType.password
})
.then( (r) => {
.then((r) => {
if (!r.result) return;
this._manager
.loan(Database.current(),
this._account,
r.text,
this.get('item').getBarcode())
.then( (result) => {
this._account,
r.text,
this.get('item').getBarcode())
.then((result) => {
if (result['success'])
return this.dialogs.alert({title: L('confirm'),
message: L('loan_title_date_due',
result['title'],
new Iso8601Date(result['due_date']).asLongDate()),
okButtonText: L('OK')});
return this.dialogs.confirm( { title: L('error'),
message: result['message'],
okButtonText: L('OK')});
})
return this.dialogs.alert({
title: L('confirm'),
message: L('loan_title_date_due',
result['title'],
new Iso8601Date(result['due_date']).asLongDate()),
okButtonText: L('OK')
});
return this.dialogs.confirm({
title: L('error'),
message: result['message'],
okButtonText: L('OK')
});
})
})
.then( () => {
.then(() => {
this.goBack();
});
}
......@@ -229,13 +246,13 @@ export class LibraryController extends Controller {
openSettingsIfPermissionWasPreviouslyDenied: true
})
.then(
(result) => {
this._account.credentials['login'] = result.text;
this._askLoginPassword();
},
(result) => {
this._account.credentials['login'] = result.text;
this._askLoginPassword();
},
(error) => {
});
(error) => {
});
}
......@@ -294,11 +311,12 @@ export class LibraryController extends Controller {
._loadLoansAndHolds()
._turnBusyIndicator('off')
})
.then( () => {
return this._portal.updateAccountInfo(this._account);})
.then( () => {
.then(() => {
return this._portal.updateAccountInfo(this._account);
})
.then(() => {
this.set('card', this._account.getCard());
})
......@@ -329,7 +347,7 @@ export class LibraryController extends Controller {
public onOauthPageLoaded(args) {
let webview = args.object.getViewById("webView");
let oauth_web_view = (new OAuthWebViewClient(webview));
let oauth_web_view = OAuthWebViewClient.newWithWebView(webview);
this._doOAuthLogin(oauth_web_view);
}
......@@ -339,9 +357,11 @@ export class LibraryController extends Controller {
Database.current().save(this._account);
this.navigate(
{ moduleName: "views/accounts/list",
{
moduleName: "views/accounts/list",
bindingContext: (new AccountsController(this._manager)).showAddAccountHint(),
clearHistory: true });
clearHistory: true
});
}
......@@ -349,28 +369,32 @@ export class LibraryController extends Controller {
this.set('account', this._account)
return this
}
protected _authenticationDone(account:Account): LibraryController {
protected _authenticationDone(account: Account): LibraryController {
Database.current().save(account);
this.set('busy', true);
this._portal
.signIn(account)
.then(() => {
this.set('busy', false);
this._on_loans_page_loaded = this._on_successful_login;
this.set('account_label', account.label);
this.navigate({ moduleName: "views/library/account_label",
bindingContext: this });
this.navigate({
moduleName: "views/library/account_label",
bindingContext: this
});
})
.catch((error) => {
this.set('busy', false);
this.dialogs
.alert({title: L('error'),
message: L('wrong_login_password'),
okButtonText: L('OK')})
.then( () => {
.alert({
title: L('error'),
message: L('wrong_login_password'),
okButtonText: L('OK')
})
.then(() => {
this._onPortalError(error);
});
});
......@@ -382,17 +406,19 @@ export class LibraryController extends Controller {
oauth_web_view
.login(this._account)
.then(
(token) => {
this._account.credentials['token'] = token;
this._authenticationDone(this._account);
},
() => {
this.dialogs.alert({title: L('alert'),
message: L('wrong_login_password'),
okButtonText: L('OK')});
this._doOAuthLogin(oauth_web_view);
}
(token) => {
this._account.credentials['token'] = token;
this._authenticationDone(this._account);
},
() => {
this.dialogs.alert({
title: L('alert'),
message: L('wrong_login_password'),
okButtonText: L('OK')
});
this._doOAuthLogin(oauth_web_view);
}
);
}
......@@ -402,8 +428,10 @@ export class LibraryController extends Controller {
let doOpenWebsite = () => {
new Cookies().syncHttpToWebViews();
this.navigate({ moduleName: "views/library/website",
bindingContext: this });
this.navigate({
moduleName: "views/library/website",
bindingContext: this
});
this.set('website_url', website_url);
return this._turnBusyIndicator('off');
};
......@@ -436,25 +464,31 @@ export class LibraryController extends Controller {
return this._askLoginPassword();
console.dir(error.stack);
this.dialogs.alert({title: L(error.name),
message: error.message,
okButtonText: L('OK')});
this.dialogs.alert({
title: L(error.name),
message: error.message,
okButtonText: L('OK')
});
}
protected _askLoginPassword(): this {
this.set('credentials', this._account.credentials);
this.navigate({ moduleName: "views/library/login-password",
bindingContext: this,
backstackVisible: false });
this.navigate({
moduleName: "views/library/login-password",
bindingContext: this,
backstackVisible: false
});
return this;
}
protected _registerOnOAuth(url: string) {
this.navigate({ moduleName: "views/library/oauth",
bindingContext: this,
backstackVisible: false });
this.navigate({
moduleName: "views/library/oauth",
bindingContext: this,
backstackVisible: false
});
}
......@@ -471,9 +505,9 @@ export class LibraryController extends Controller {
._updateItems(this._holds, this._account.findHolds(Database.current()))
}
protected _updateItems(obs_items: ObservableArray<ItemOperation>, items: Array<ItemOperation>): this {
while (obs_items.length) { obs_items.pop(); }
while (obs_items.length) { obs_items.pop(); }
items.forEach((item) => {
obs_items.push(item);
......@@ -483,33 +517,33 @@ export class LibraryController extends Controller {
}
protected _turnBusyIndicator(state:string): this {
protected _turnBusyIndicator(state: string): this {
this.set('busy', state);
if (!this._view)
return this;
let loans = this._view.getViewById("loans")
if (loans != undefined) {
loans.notifyPullToRefreshFinished()
loans.refresh()
}
let holds = this._view.getViewById("holds")
if (holds != undefined) {
holds.notifyPullToRefreshFinished()
holds.refresh()
}
let indicator = this._view.getViewById("activity-indicator");
let indicator = this._view.getViewById("activity-indicator");
if (!indicator || (undefined == indicator.animate))
return this;
indicator.animate({
opacity: (state == 'on' ? 1 : 0 ),
opacity: (state == 'on' ? 1 : 0),
duration: 1000
});
return this;
}
}
}
import {Account, Cookies} from '../../models';
import {WebView} from 'ui/web-view';
import { Account, Cookies } from '../../models';
import { WebView } from 'ui/web-view';
export class OAuthWebViewClient {
protected static _onTokenSet: any;
protected static _onError: any;
protected _view: WebView;
public constructor(view:WebView) {
public static newWithWebView(webview: WebView): OAuthWebViewClient {
return new OAuthWebViewClient(webview);
}
public constructor(view: WebView) {
this._view = view;
let WVClient = (<any>android.webkit.WebViewClient).extend({
shouldOverrideUrlLoading: (_webview, page_url) => {
if (typeof page_url === 'object') // android 7.1.1 compatibility
......@@ -16,8 +21,8 @@ export class OAuthWebViewClient {
let matches = page_url.match(/.*token=(.*)$/);
if (matches) {
let token = matches[1];
OAuthWebViewClient._onTokenSet(token);
let token = matches[1];
OAuthWebViewClient._onTokenSet(token);
new Cookies().clearCookies();
return true;
}
......@@ -27,14 +32,15 @@ export class OAuthWebViewClient {
return true;
}
return false;
return false;
},
onPageFinished: (_webview, page_url) => {
},
onPageStarted: (_webview, page_url, favicon) => {
}});
}
});
this._view.android.setWebViewClient(new WVClient());
}
......@@ -48,9 +54,9 @@ export class OAuthWebViewClient {
OAuthWebViewClient._onError = reject;
this._view.src = account.getUrl()
+ '/auth/oauth/response_type/code/id_profil/1/client_id/'
+ L('app_name')
+ '?redirect_uri=bibapp://authorize';
+ '/auth/oauth/response_type/code/id_profil/1/client_id/'
+ L('app_name')
+ '?redirect_uri=bibapp://authorize';
});
}
}
import {Account} from '../../models';
import { Account } from '../../models';
import { WebView } from 'ui/web-view';
export declare class OAuthWebViewClient {
public constructor(view:WebView);
public login(account: Account): Promise<string>;
public static newWithWebView(webview: WebView): OAuthWebViewClient;
public login(account: Account): Promise<string>;
}
import {Account} from '../../models';
import {WebView} from 'ui/web-view';
import { Account, Cookies } from '../../models';
import { WebView } from 'ui/web-view';
var NSWKNavigationDelegate = (NSObject as any).extend(
{
_owner: null,
_origDelegate: null,
_onTokenSet: null,
_onError: null,
webViewDidStartLoad: function(webview: WKWebView) {
console.log('start Load');
return this._origDelegate.webViewDidStartLoad(webview);
},
webViewDidStartProvisionalNavigation: function(webview, navigation) {
console.log('start prov url:' + webview.URL.absoluteString);
return this._origDelegate.webViewDidStartProvisionalNavigation(webview, navigation);
},
webViewDidFailNavigationWithError: function(webview: WKWebView, navigation: WKNavigation, error: NSError) {
console.log('didFailNavigationWithError');
return this._origDelegate.webViewDidFailNavigationWithError(webview, navigation, error);
},
webViewDecidePolicyForNavigationActionDecisionHandler: function(webview: WKWebView, navigationAction: WKNavigationAction, decisionHandler: WKNavigationActionPolicy) {
console.log('decidePolicyForNavigationActionDecisionHandler:' + webview.URL.absoluteString);
let page_url = webview.URL.absoluteString;
let matches = page_url.match(/.*token=(.*)$/);