Well I’m back again, with part 4 of this series. No, that’s not a typo in the title, this post will be primarily about revision 5 and revision 6 of my bzr branch. What happened to rev 4? Well it was pretty boring to be honest, just removing some console.log() calls that I used as a poor excuse for a debugger. Anyway, boring.
If you haven’t read the previous articles in this series, you’ll want to do that before reading any further here:
Comments
Everybody knows that comments are half the fun of Reddit, but up until now uReadIt wasn’t able to view them. Now, the proper way to do this would be to use the Reddit API to download the comment threads, and load them using nested ListViews. But that’s going to take a while, and I wanted comments now. So I cheated, took the easy way out, and just used the existing WebView to load the Reddit comments page URL instead. I’ll do it the right way in a later revision….probably.
The first thing I needed was a way to load comments instead of the article content. This meant finally using the Comments toolbar action I put in place earlier. But I needed a way to change back too, nobody likes a one-way trip, so I added an Article action as well.
Action {
id: commentAction
objectName: "comment"
visible: true
iconSource: Qt.resolvedUrl("comments.png")
text: i18n.tr("Comments")
onTriggered: {
articleContent.showComments()
articleAction.enabled = true
commentAction.enabled = false
articleViewActions.active = false
}
}
Action {
id: articleAction
objectName: "article"
enabled: false
iconSource: Qt.resolvedUrl("avatar.png")
text: i18n.tr("Article")
onTriggered: {
articleContent.showArticle()
commentAction.enabled = true
articleAction.enabled = false
articleViewActions.active = false
}
}
Then I had to write the showComments and showArticle functions, which would switch the WebView.url from one to the other. There was just one problem, that code didn’t have the comments url, only the content url. So first I had to pass more data to my articleView page. To avoid having to do this again, I decided to just pass it the whole article data model that I was getting from JSONListModel, that way I would have all the data I could potentially need for future features.
Since I created a new property called article, I also get a callback handler called onArticleChanged, which I took advantage of to determine if an article’s content was already a link to a Reddit comment page, and if so disabling the option to switch between Article and Comments.
Page {
id: articleView
title: 'Article'
property var article: undefined
onArticleChanged: {
if (article) {
articleContent.article = article
commentAction.enabled = !article.data.is_self
articleAction.enabled = false
articleView.title = article.data.title
articleContent.visible = true
}
}
Now I could finally implement showComments and showArticle, which I decided to do inside of ArticleView. To support that, I would also need to pass the article data model on again, this time to ArticleView. Then I could use that data to switch the WebView’s url.
Item {
property var article: undefined
property string baseUrl: 'http://www.reddit.com'
onArticleChanged: {
if (article) {
articleWebView.url = article.data.url
}
}
...
function showComments() {
console.log('Comments: '+baseUrl + article.data.permalink)
articleWebView.url = baseUrl + article.data.permalink
}
function showArticle() {
articleWebView.url = article.data.url
}
Subreddit Filters
I usually only read the Hot subreddit filter, I’ve only used New a handful of times, but like I said in the first article in this series, I’m going things to learn the Ubuntu SDK, not make a Reddit app. I wanted to write some code that used the Ubuntu Popups.Popover component, and changing Reddit filters seemed like a good use for that kind of component.
Like Popups.Dialog, using a Popover is relatively simple. You start with a Component to contain your popup, add your components to it, then call PopupUtils.open. For changing filters, I chose to just put in a Column filled with ListItem.Standard items, one for each filter. When one of them is selected, it will change a new filter property on my SubredditListView (which will reload from Reddit using the new filter).
Component {
id: popoverComponent
Popups.Popover {
id: popover
Column {
id: containerLayout
...
ListItem.Standard {
text: "Hot"
selected: articleList.filter == 'hot'
onClicked: {
articleList.filter = 'hot'
PopupUtils.close(popover)
}
}
ListItem.Standard {
text: "New"
selected: articleList.filter == 'new'
onClicked: {
articleList.filter = 'new'
PopupUtils.close(popover)
}
}
ListItem.Standard {
text: "Rising"
selected: articleList.filter == 'rising'
onClicked: {
articleList.filter = 'rising'
PopupUtils.close(popover)
}
}
ListItem.Standard {
text: "Controversial"
selected: articleList.filter == 'controversial'
onClicked: {
articleList.filter = 'controversial'
PopupUtils.close(popover)
}
}
ListItem.Standard {
text: "Top"
selected: articleList.filter == 'top'
onClicked: {
articleList.filter = 'top'
PopupUtils.close(popover)
}
}
}
}
}
tools: ToolbarActions {
...
Action {
id: filterAction
objectName: "filterAction"
iconSource: Qt.resolvedUrl("settings.png")
text: i18n.tr("Filter")
onTriggered: {
PopupUtils.open(popoverComponent, filterAction.itemHint)
}
}
}
Packaging
Finally I was ready to package uReadIt, to make it easy to install. I copied my packaging files from what was used by the Ubuntu Touch Core Apps, which was itself copied from packaging files used by the notepad-qml app. Now I’ll admit, it’s not perfect, and we’ve already had patches submitted to fix the Core Apps packaging, changes which I will be applying to uReadIt at some point. So don’t take these as the right way to package your app, I’m putting them here to explain in a broad sense what the different files do in a Debian package.
debian/control
The control file gives all of the data about your package. It has two sections, the first is for the source package, it contains the source package name, list of dependent packages needed to build your app and package, and some other miscellaneous information used by the packaging system. Below that will be one or more binary package definitions. I only have one, you probably will too. This section contains another list of dependent packages, but these are packages needed to run your app, not build it. It also contains a space to describe your application. The first line of the Description should be a brief description, used when listing a lot of packages together, and the lines below it should have a longer description, used when showing more information about a single package.
Source: ureadit
Priority: extra
Maintainer: Michael Hall
Build-Depends: debhelper (>= 8.0.0),
Standards-Version: 3.9.4
Section: misc
Homepage: https://launchpad.net/~mhall119/
Package: ureadit
Section: misc
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends},
qmlscene,
qtdeclarative5-ubuntu-ui-toolkit-plugin | qt-components-ubuntu,
qtdeclarative5-qtquick2-plugin
Description: Reddit Browser
Desktop application for browsing Reddit, it's articles and comments
debian/rules
The rules file is what actually does the building, it’s like a Makefile for your package. Ideally this doesn’t do much more than calling dh (debhelper). In fact, mine should be doing that, but it has a lot of unnecessary complication due to being copied from one project to another to another. You’re probably better of just ignoring mine, just remember that debian/rules does the building.
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# Work-around for some machines where INSTALL_ROOT is not set properly by
# dh_auto_install
override_dh_auto_install:
dh_auto_install -- INSTALL_ROOT=$(CURDIR)/debian/tmp
# Workaround a bug in that debhelper package version
override_dh_install:
mkdir -p $(CURDIR)/debian/tmp/usr/share/applications/
mkdir -p $(CURDIR)/debian/tmp/usr/bin/
mkdir -p $(CURDIR)/debian/tmp/usr/share/uReadIt/
cp uReadIt.desktop $(CURDIR)/debian/tmp/usr/share/applications/
cp uReadIt.bin $(CURDIR)/debian/tmp/usr/bin/uReadIt
cp -r *.qml *.js *.png $(CURDIR)/debian/tmp/usr/share/uReadIt/
dh_install --sourcedir=debian/tmp --fail-missing
%:
dh $@
debian/changelog
The changelog file contains a record of revisions to your package, just like a bzr or git changelog. More importantly, the changelog is what tells debhelper the lasted version of your package. So the (0.3) in the top line on mine tells it to build ureadit_0.3_all.deb. It will also use the signature line to try and find a matching GPG key when signing packages.
ureadit (0.3) raring; urgency=low
* Initial release
-- Michael Hall Mon, 25 March 2013 23:09:00 -0400
debian/copyright
Making sure that FOSS packages can be distributed, modified, and redistributed is important, so in Debian and Ubuntu having a properly formed copyright file is a requirement. I won’t go into much detail on how to do this, the link at the top will take you to the official spec. The key pieces are the sections that give a file glob, license and attribution. You can have as many of these sections as you need to properly cover all of your app.
Format: http://dep.debian.net/deps/dep5
Upstream-Name: uReadIt
Source:
Files: *
Copyright: 2013 Michael Hall
License: GPL-3.0
Files: debian/*
Copyright: 2013 Michael Hall
License: LGPL-3.0
Next time: Customizing headers
The stock Ubuntu component Headers are nice, but they weren’t serving my purposes. I wanted them to display more text, and ideally take up less room. So in the next revision, I replaced them with some custom components that did exactly what I wanted.