Skip to content
This repository was archived by the owner on Aug 4, 2023. It is now read-only.

Commit 90367c0

Browse files
committed
Initial news page
1 parent e8e4ca7 commit 90367c0

19 files changed

+363
-17
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ hooks/
1414
*.apk
1515
*.ipa
1616

17-
Telerik.keystore
17+
Telerik.keystore
18+
19+
GoogleService-Info.plist
20+
google-services.json

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
// Place your settings in this file to overwrite default and user settings.
22
{
3+
"typescript.tsdk": "./node_modules/typescript/lib"
34
}

FIREBASE.ms

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Updating the news in firebase manually:
2+
Update the firebase-database.json with the added articles and "import" in firebase.
3+
4+
Sending push notifications:
5+
Make sure to set:
6+
- Message text: the message text
7+
- Message label: message title displayed in firebase
8+
In the Advanced:
9+
- Title: the message title (this is important!)
10+
- In custom data add the fields:
11+
- inAppTitle: repeat the "Title"
12+
- inAppBody: repeat the "Message text"
13+
- url: link to the page to be opened on tap
14+

app/common/firebase.ts

Lines changed: 134 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,123 @@ import * as utils from "utils/utils";
33
import * as dialogs from "ui/dialogs";
44
import {isAndroid, isIOS} from "platform";
55
import * as settings from "application-settings";
6+
import { Observable } from "data/observable";
7+
import * as navigator from "../common/navigator";
68

7-
const enabled = false;
9+
const enabled = true;
10+
11+
interface FirebaseArticle {
12+
readonly id: string;
13+
readonly title: string;
14+
readonly body: string;
15+
readonly url: string;
16+
readonly date: string;
17+
}
18+
19+
const FIREBASE_NEWS_READ_KEY = "firebase:news:read";
20+
let _readArticles;
21+
function loadReadArticles() {
22+
if (_readArticles) {
23+
return _readArticles;
24+
}
25+
26+
const str = settings.getString(FIREBASE_NEWS_READ_KEY, "{}");
27+
try {
28+
_readArticles = JSON.parse(str);
29+
} catch(e) {
30+
console.log("Failed to parse read firebase articles.");
31+
_readArticles = {};
32+
}
33+
34+
return _readArticles;
35+
}
36+
37+
function saveReadArticles() {
38+
try {
39+
settings.setString(FIREBASE_NEWS_READ_KEY, JSON.stringify(loadReadArticles()));
40+
} catch(e) {
41+
console.log("Failed to persist read articles: " + e);
42+
}
43+
}
44+
45+
function getIsArticleRead(id: string): boolean {
46+
return !!loadReadArticles()[id];
47+
}
48+
49+
function markAsRead(id: string) {
50+
loadReadArticles()[id] = true;
51+
saveReadArticles();
52+
}
53+
function markAsUnread(id: string) {
54+
loadReadArticles()[id] = false;
55+
saveReadArticles();
56+
}
57+
58+
export class Article extends Observable {
59+
readonly id: string;
60+
readonly title: string;
61+
readonly body: string;
62+
readonly url: string;
63+
readonly date: string;
64+
65+
constructor(base: FirebaseArticle) {
66+
super();
67+
this.id = base.id;
68+
this.title = base.title;
69+
this.body = base.body;
70+
this.url = base.url;
71+
this.date = base.date;
72+
}
73+
74+
get isRead(): boolean {
75+
return !!(this.id && getIsArticleRead(this.id));
76+
}
77+
set isRead(value: boolean) {
78+
if (value && this.id) {
79+
markAsRead(this.id);
80+
} else {
81+
markAsUnread(this.id);
82+
}
83+
this.notifyPropertyChange("isRead", value);
84+
viewModel.updateHasUnreadNews();
85+
}
86+
}
87+
88+
export class ViewModel extends Observable {
89+
news: Article[];
90+
hasNews: boolean = false;
91+
hasUnreadNews: boolean = false;
92+
93+
constructor() {
94+
super();
95+
this.updateNews([]);
96+
}
97+
98+
updateNews(updates: FirebaseArticle[]) {
99+
this.news = updates.map(fb => new Article(fb));
100+
this.updateHasNews();
101+
this.updateHasUnreadNews();
102+
this.notifyPropertyChange("news", this.news);
103+
}
104+
105+
updateHasNews() {
106+
let newValue = this.news && this.news.length > 0;
107+
if (newValue != this.hasNews) {
108+
this.hasNews = newValue;
109+
this.notifyPropertyChange("hasNews", newValue);
110+
}
111+
}
112+
113+
updateHasUnreadNews() {
114+
let newValue = this.news && this.news.length > 0 && this.news.some(args => !args.isRead);
115+
if (newValue != this.hasUnreadNews) {
116+
this.hasUnreadNews = newValue;
117+
this.notifyPropertyChange("hasUnreadNews", newValue);
118+
console.log("hasUnreadNews: " + this.hasUnreadNews);
119+
}
120+
}
121+
}
122+
export const viewModel = new ViewModel();
8123

9124
var firebase;
10125
var lastHandledData;
@@ -42,6 +157,7 @@ function firebaseInit() {
42157
console.log("Firebase init!!!");
43158

44159
firebase.init({
160+
persist: true,
45161
onMessageReceivedCallback(message) {
46162
console.log("Got message!");
47163
console.log(JSON.stringify(message));
@@ -52,16 +168,18 @@ function firebaseInit() {
52168
dialogs.confirm({
53169
title: (<any>message).inAppTitle,
54170
message: (<any>message).inAppBody,
55-
okButtonText: "Open",
56-
cancelButtonText: "Close"
171+
okButtonText: isAndroid ? "OPEN" : "Open",
172+
cancelButtonText: isAndroid ? "OK" : "OK"
57173
}).then(result => {
58174
if (result) {
59175
utils.openUrl(url);
176+
// TODO: Mark as read!!!
177+
navigator.navigateToWhatIsNew();
60178
}
61179
});
62180
} else {
63181
if (lastHandledData != url) {
64-
utils.openUrl(url);
182+
navigator.navigateToWhatIsNew();
65183
lastHandledData = url;
66184
}
67185
}
@@ -73,7 +191,18 @@ function firebaseInit() {
73191
settings.setBoolean("user-granted-push", true);
74192
}
75193
}).then(value => {
76-
console.log("Firebase init done!");
194+
firebase.addValueEventListener((result) => {
195+
if (!result.error) {
196+
console.log("Update news: " + JSON.stringify(result.value))
197+
viewModel.updateNews(result.value || []);
198+
} else {
199+
console.log(JSON.stringify(result));
200+
}
201+
}, "/news").then((listenerWrapper) => {
202+
const path = listenerWrapper.path;
203+
const listeners = listenerWrapper.listeners
204+
console.log("Listening for firebase data. path: " + path + ", listeners: " + listeners);
205+
});
77206
}).catch(e => {
78207
console.log("Failed to init firebase. " + e);
79208
console.log("stack:\n" + e.stack);

app/common/navigator.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ export function navigateToAbout() {
124124
}
125125
}
126126

127+
export function navigateToWhatIsNew() {
128+
var topmost = frame.topmost();
129+
if (topmost.currentEntry.moduleName !== "views/what-is-new") {
130+
frame.topmost().navigate(traceNavigateTo("views/what-is-new"));
131+
}
132+
}
133+
127134
export function navigateBack() {
128135
frame.goBack();
129136
}

app/examples/listview/selection/selection-view-model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export class SelectionViewModel extends Observable {
208208
if (this.isSelectionActive === true || this.isReorderActive === true) {
209209
return;
210210
}
211-
this.CurrentItem = (<ObservableArray<BlogPostItemData>>this._owner.items).getItem(args.itemIndex);
211+
this.CurrentItem = (<any>this._owner.items).getItem(args.itemIndex);
212212
this._currentItemIndex = args.itemIndex;
213213
topmostFrame().navigate({
214214
moduleName: "examples/listview/selection/detail-page",

app/view-models/main-page-view-model.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import examplesVM = require("./examples-model");
22
import observable = require("data/observable");
33
import paltfrom = require("platform");
4+
import { viewModel } from "../common/firebase";
45

56
export class MainPageViewModel extends observable.Observable {
7+
8+
get firebase() {
9+
return viewModel;
10+
}
11+
612
get exampleGroups(): Array<examplesVM.ExampleGroup> {
713
return examplesVM.groups;
814
}

app/views/main-page/main-page.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@
2121
<sdp:DrawerPage.actionBar>
2222
<ActionBar automationText="ActionBar" android:opacity="0">
2323
<android>
24-
<NavigationButton icon="res://ic_menu_main" tap="showSlideout" automationText="SidebarMenu" />
24+
<NavigationButton icon="{{ firebase.hasUnreadNews ? 'res://ic_new' : 'res://ic_menu_main' }}" tap="showSlideout" automationText="SidebarMenu" />
2525
</android>
26-
<iOS>
26+
<!--<iOS>
2727
<NavigationButton icon="res://ic_back" />
28-
</iOS>
28+
</iOS>-->
2929
<ActionBar.actionItems>
3030
<iOS>
3131
<ActionItem id="actionbar-menu" ios.position="left" tap="showSlideout" automationText="SidebarMenu">
3232
<ActionItem.actionView>
33-
<Image src="res://ic_menu_main" width="22" height="22" margin="0, 8, 0, -8" />
33+
<Image src="{{ firebase.hasUnreadNews, firebase.hasUnreadNews ? 'res://ic_new' : 'res://ic_menu_main' }}" width="22" height="22" margin="0, 8, 0, -8" />
3434
</ActionItem.actionView>
3535
</ActionItem>
3636
</iOS>

app/views/side-drawer-content/side-drawer-content-common.css

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
border-color: #3D5AFE;
2323
}
2424

25-
.drawer-external-link {
25+
.drawer-external-link,
26+
.drawer-news-link {
2627
height: 44;
2728
}
28-
.drawer-external-link:highlighted {
29+
30+
.drawer-external-link:highlighted,
31+
.drawer-news-link:highlighted {
2932
background-color: #516BFE;
3033
}
3134
.drawer-external-icon {
@@ -35,6 +38,14 @@
3538
horizontal-align: center;
3639
padding: 0 36;
3740
}
41+
.drawer-news-icon {
42+
background-position: 0% 50%;
43+
background-repeat: no-repeat;
44+
horizontal-align: center;
45+
padding: 0 36;
46+
}
47+
48+
.drawer-news-label,
3849
.drawer-external-label {
3950
font-size: 13;
4051
color: white;

app/views/side-drawer-content/side-drawer-content.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@ import { GroupPageViewModel } from "../../view-models/group-page-view-model"
33
import * as navigator from "../../common/navigator"
44
import * as gestures from "ui/gestures";
55
import { groups } from "../../view-models/examples-model"
6+
import * as firebase from "../../common/firebase";
67
import { topmost } from "ui/frame"
78
import { grayTouch } from "../../common/effects";
89
import * as application from "application";
10+
import * as applicationSettings from "application-settings";
11+
import { Observable } from "data/observable";
12+
13+
class SidedrawerViewModel extends Observable {
14+
public groups = groups;
15+
public firebase = firebase.viewModel;
16+
}
917

1018
export function onLoaded(args) {
11-
args.object.bindingContext = groups;
19+
args.object.bindingContext = new SidedrawerViewModel();
1220
}
1321

1422
export function tileTouch(args: gestures.TouchGestureEventData) {
@@ -58,6 +66,15 @@ export function tapAbout(args) {
5866
}
5967
}
6068

69+
export function tapWhatIsNew() {
70+
closeDrawer();
71+
if (application.android) {
72+
setTimeout(() => navigator.navigateToWhatIsNew(), 600);
73+
} else {
74+
navigator.navigateToWhatIsNew();
75+
}
76+
}
77+
6178
export function tapDrawerLink(args) {
6279
closeDrawer();
6380
navigator.openLink(args.object);

app/views/side-drawer-content/side-drawer-content.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
<ScrollView class="drawer-content">
2-
<StackLayout loaded="onLoaded">
2+
<StackLayout loaded="onLoaded" unloaded="onUnloaded">
33
<Image src="res://logo_main_nav" margin="40 0 24 0" horizontalAlignment="center" stretch="none" />
44

55
<Button text="Home" tap="tapHome" class="drawer-button" />
66
<Button text="About" tap="tapAbout" class="drawer-button" />
77

8+
<GridLayout class="drawer-news-link" tap="tapWhatIsNew">
9+
<GridLayout class="drawer-news-icon">
10+
<Image src="res://ic_outside_link" visibility="{{ firebase.hasUnreadNews, firebase.hasUnreadNews ? 'visible' : 'collapse' }}" width="22" height="22" horizontalAlignment="left" />
11+
<Label text="What's new" class="drawer-news-label" margin="0 22" />
12+
</GridLayout>
13+
</GridLayout>
14+
815
<GridLayout class="drawer-external-link" tap="tapDrawerLink" tag="http://docs.nativescript.org/getting-started">
916
<GridLayout class="drawer-external-icon">
1017
<Label text="Getting started" class="drawer-external-label" />

app/views/what-is-new-common.css

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
.title {
2+
color: #151f2f;
3+
}
4+
.body {
5+
white-space: normal;
6+
margin: 6 0;
7+
color: #455b66;
8+
}
9+
.learn-more {
10+
vertical-align: bottom;
11+
color: #3c5afd;
12+
font-family: sans-serif;
13+
font-weight: 500;
14+
}
15+
.date {
16+
vertical-align: bottom;
17+
color: #969da1;
18+
}
19+
20+
.news-list,
21+
.news-background {
22+
background-color: #455B66;
23+
}
24+
25+
.news-card {
26+
padding: 10;
27+
}
28+
.news-card.read {
29+
background-color: #BCC3C7;
30+
}
31+
32+
.news-card.unread {
33+
background-color: white;
34+
}
35+
36+
.empty-placeholder {
37+
margin: 16;
38+
color: #b2b9bd;
39+
white-space: normal;
40+
font-size: 17;
41+
}

0 commit comments

Comments
 (0)