Added cuteness.unoptimized app.

This commit is contained in:
Rossen Hristov
2015-07-02 16:25:10 +03:00
parent 7cf7c66302
commit f98a14f073
17 changed files with 404 additions and 1 deletions

View File

@@ -0,0 +1,7 @@
import application = require("application");
// Set the start module for the application
application.mainModule = "main-page";
// Start the application
application.start();

View File

@@ -0,0 +1,14 @@
Page {
background-color: #363940;
}
.detailsTitle {
color: #FFFFFF;
font-size: 30;
margin: 10;
}
ActivityIndicator {
width: 50;
height: 50;
}

View File

@@ -0,0 +1,10 @@
import pages = require("ui/page");
import observable = require("data/observable");
// Event handler for Page "navigatedTo" event attached in details-page.xml
export function pageNavigatedTo(args: observable.EventData) {
// Get the event sender
var page = <pages.Page>args.object;
page.bindingContext = page.navigationContext;
}

View File

@@ -0,0 +1,24 @@
<Page xmlns="http://www.nativescript.org/tns.xsd" navigatedTo="pageNavigatedTo">
<GridLayout rows="*, auto">
<ScrollView>
<StackLayout>
<Image imageSource="{{ imageSource }}" stretch="aspectFill"/>
<Label text="{{ title }}" cssClass="detailsTitle" textWrap="true" />
</StackLayout>
</ScrollView>
<StackLayout orientation="horizontal" row="1">
<Label margin="2">
<Label.formattedText>
<FormattedString fontSize="20" foregroundColor="#C6C6C6">
<FormattedString.spans>
<Span text="{{ author ? 'by ' + author : '' }}"/>
<Span text="{{ num_comments ? ' | ' : '' }}" />
<Span text="{{ num_comments ? num_comments + ' comments' : '' }}" foregroundColor="#10C2B0"/>
</FormattedString.spans>
</FormattedString>
</Label.formattedText>
</Label>
</StackLayout>
<ActivityIndicator busy="{{ isLoading }}" />
</GridLayout>
</Page>

View File

@@ -0,0 +1,29 @@
.title {
font-size: 20;
margin: 3;
}
.author {
font-size: 14;
horizontal-align: left;
vertical-align: bottom;
margin: 3;
}
.comments {
color: #10C2B0;
font-size: 14;
vertical-align: bottom;
margin: 3;
}
.thumbnail {
width: 72;
height: 72;
margin: 3;
vertical-align: top;
}
TabView {
background-color: white;
}

View File

@@ -0,0 +1,34 @@
import observable = require("data/observable");
import pages = require("ui/page");
import frames = require("ui/frame");
import listView = require("ui/list-view");
import redditAppViewModel = require("./reddit-app-view-model");
var appViewModel = new redditAppViewModel.AppViewModel();
// Event handler for Page "loaded" event attached in main-page.xml
export function pageLoaded(args: observable.EventData) {
// Get the event sender
var page = <pages.Page>args.object;
page.bindingContext = appViewModel;
appViewModel.loadItemsAsync();
// Enable platform specific feature (in this case Android page caching)
if (frames.topmost().android) {
frames.topmost().android.cachePagesOnNavigate = true;
}
}
export function listViewItemTap(args: listView.ItemEventData) {
// Navigate to the details page with context set to the data item for specified index
frames.topmost().navigate({
moduleName: "./details-page",
context: appViewModel.redditItems.getItem(args.index)
});
}
export function listViewLoadMoreItems(args: observable.EventData) {
appViewModel.loadItemsAsync();
}

View File

@@ -0,0 +1,30 @@
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded">
<TabView>
<TabView.items>
<TabViewItem title="List">
<TabViewItem.view>
<ListView items="{{ redditItems }}" itemTap="listViewItemTap" loadMoreItems="listViewLoadMoreItems">
<ListView.itemTemplate>
<!-- Binding in template property of an component will use the bindingContext provided by the component. -->
<GridLayout columns="auto, *, auto" rows="auto, 25">
<Image src="{{ thumbnailImage }}" cssClass="thumbnail" rowSpan="2"/>
<Label text="{{ title || 'Downloading...' }}" textWrap="true" cssClass="title" col="1" colSpan="2" minHeight="50" />
<Label text="{{ author ? 'by ' + author : '' }}" cssClass="author" col="1" row="1" />
<Label text="{{ num_comments ? num_comments + ' comments' : '' }}" cssClass="comments" col="2" row="1" />
</GridLayout>
<!-- End of tempplate. -->
</ListView.itemTemplate>
</ListView>
</TabViewItem.view>
</TabViewItem>
<TabViewItem title="About">
<TabViewItem.view>
<StackLayout>
<Image margin="10" src="~/res/telerik-logo.png" />
<Label margin="10" textWrap="true" text="{{ aboutText }}" />
</StackLayout>
</TabViewItem.view>
</TabViewItem>
</TabView.items>
</TabView>
</Page>

View File

@@ -0,0 +1,2 @@
{ "name" : "cuteness.unoptimized",
"main" : "app.js" }

View File

@@ -0,0 +1,67 @@
import imageSource = require("image-source");
import observableArray = require("data/observable-array");
import http = require("http");
import observable = require("data/observable");
import imageCache = require("ui/image-cache");
import redditModel = require("./reddit-model");
import redditViewModel = require("./reddit-item-view-model");
var aboutText = "Cuteness is a proof of concept app demonstrating the Telerik's NativeScript for writing native mobile applications using JavaScript.";
export var defaultThumbnailImageSource = imageSource.fromFile("~/res/reddit-logo.png");
export var defaultNoThumbnailImageSource = imageSource.fromFile("~/res/no-image.png");
var redditUrl = "http://www.reddit.com/r/aww.json?limit=50";
// initialize the image cache for the main list
export var cache = new imageCache.Cache();
cache.placeholder = defaultThumbnailImageSource;
cache.maxRequests = 5;
export class AppViewModel extends observable.Observable {
private _redditItems = new observableArray.ObservableArray<redditViewModel.RedditViewModel>();
private _isLoading = false;
private _after = "";
get redditItems(): observableArray.ObservableArray<redditViewModel.RedditViewModel> {
return this._redditItems;
}
public loadItemsAsync() {
if (this._isLoading) {
return;
}
this._isLoading = true;
var requestUrl = redditUrl + this._after;
//console.log(`>>> Requesting ${requestUrl}`);
var that = this;
http.getJSON<redditModel.Data>(requestUrl).then(result => {
//console.log(`>>> Received ${result.data.children.length} reddit items`);
var newItems = result.data.children.map(i=> {
return new redditViewModel.RedditViewModel(i.data);
});
that._redditItems.push(newItems);
that._after = "&after=" + newItems[newItems.length - 1].source.name;
that._isLoading = false;
},(e) => {
console.log(e.message);
that._isLoading = false;
})
.catch(function (e) {
that._isLoading = false;
setTimeout(function () { throw e; });
});
}
get aboutText(): string {
return aboutText;
}
get defaultThumbnailImageSource(): imageSource.ImageSource {
return defaultThumbnailImageSource;
}
get defaultNoThumbnailImageSource(): imageSource.ImageSource {
return defaultNoThumbnailImageSource;
}
}

View File

@@ -0,0 +1,101 @@
import observable = require("data/observable");
import imageSource = require("image-source");
import redditModel = require("./reddit-model");
import redditAppViewModel = require("./reddit-app-view-model");
var firstThumbnailImageSource = imageSource.fromFile("~/res/first-image.png");
var defaultImageSource = imageSource.fromFile("~/res/reddit-logo-transparent.png");
var ISLOADING = "isLoading";
var THUMBNAIL_IMAGE = "thumbnailImage";
var IMAGE_SOURCE = "imageSource";
export class RedditViewModel extends observable.Observable {
private _source: redditModel.ItemData;
constructor(source: redditModel.ItemData) {
super();
this._source = source;
if (this._source) {
var property: string;
for (property in this._source) {
this.set(property, this._source[property]);
}
}
}
get source(): redditModel.ItemData {
return this._source;
}
private _isLoading = false;
get isLoading(): boolean {
return this._isLoading;
}
set isLoading(value: boolean) {
if (this._isLoading !== value) {
this._isLoading = value;
this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: ISLOADING, value: value });
}
}
get thumbnailImage(): imageSource.ImageSource {
if (!this._source) {
return redditAppViewModel.defaultThumbnailImageSource;
}
if (this._source.title === "reddit 101") {
return firstThumbnailImageSource;
}
var url = this._source.thumbnail;
if (!_isValidImageUrl(url)) {
return redditAppViewModel.defaultNoThumbnailImageSource
}
var image = redditAppViewModel.cache.get(url);
if (image) {
return image;
}
this.isLoading = true;
redditAppViewModel.cache.push({
key: url,
url: url,
completed: (image: any, key: string) => {
if (url === key) {
this.isLoading = false;
this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: THUMBNAIL_IMAGE, value: image });
}
}
});
return redditAppViewModel.defaultThumbnailImageSource;
}
get imageSource(): imageSource.ImageSource {
if (this._source) {
var url = this._source.url;
if (_isValidImageUrl(url)) {
this.isLoading = true;
imageSource.fromUrl(url).then(result => {
this.isLoading = false;
this.notify({ object: this, eventName: observable.Observable.propertyChangeEvent, propertyName: IMAGE_SOURCE, value: result });
});
}
}
return defaultImageSource;
}
}
function _isValidImageUrl(url: string): boolean {
return url && (url.indexOf(".png") !== -1 || url.indexOf(".jpg") !== -1);
}

View File

@@ -0,0 +1,57 @@
export interface Data {
kind: string;
data: Items;
}
export interface Items {
modhash: string;
children: Array<Item>;
}
export interface Item {
kind: string;
data: ItemData;
}
export interface ItemData {
domain: any;
banned_by: any;
media_embed: any;
subreddit: any;
selftext_html: any;
selftext: any;
likes: any;
secure_media: any;
link_flair_text: any;
id: string;
gilded: any;
secure_media_embed: any;
clicked: boolean;
stickied: boolean;
author: any;
media: any;
score: number;
approved_by: any;
over_18: boolean;
hidden: boolean;
thumbnail: string;
subreddit_id: any;
edited: boolean;
link_flair_css_class: any;
author_flair_css_class: any;
downs: number;
saved: boolean;
is_self: boolean;
permalink: string;
name: string;
created: number;
url: string;
author_flair_text: string;
title: string;
created_utc: number;
ups: number;
num_comments: number;
visited: boolean;
num_reports: any;
distinguished: any
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB