1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Tnitter minimal portable app listing the latest Tnits
17 # This app use push notification to be updated in the second a new Tnit is posted.
18 # So it begins by requesting a full list and the successive push request for updates.
19 # If an error occurs, the full list is requested again after a short delay.
21 # This approach may still miss a few updates it they happen too close to one another.
22 # To solve this, we could send an id for the latest known Tnit to the server.
23 # Which could recognize if a client is not up to date.
26 app_version
(0, 1, git_revision
)
27 app_namespace
"net.xymus.tnitter"
31 import app
::http_request
32 import app
::data_store
34 import json
::serialization
38 # Delay in seconds before the next request after an error
39 fun request_delay_on_error
: Int do return 60
44 # Create the main window
45 window
= new TnitterWindow
54 private var layout
= new VerticalLayout(parent
=self)
55 private var list_posts
= new ListLayout(parent
=layout
)
56 private var lbl_init
= new Label(parent
=list_posts
, text
="Awaiting connection to server")
58 # Request an initial full update
59 init do (new ListPostRequest(self, "rest/list?count=16", false)).start
61 # Request a full update after a delay
62 fun request_full_list_on_error
64 (new ListPostRequest(self, "rest/list?count=16", true)).start
67 # Open a push notification connection and thread
68 fun request_push_notification
70 (new ListPostRequest(self, "push/", false)).start
73 # Update the screen to show the new `posts`
74 fun apply_update
(posts
: Array[Post])
76 layout
.remove list_posts
77 list_posts
= new ListLayout(parent
=layout
)
79 var line
= new VerticalLayout(parent
=list_posts
)
80 var author
= new LabelAuthor(parent
=line
, text
="@"+post
.user
)
81 var text
= new Label(parent
=line
, text
=post
.text
)
86 # Label to display the author's name
88 # By default, this view is identical a `Label`,
89 # but if can be refined per platforms.
90 class LabelAuthor super Label end
93 # Async RESTful actions
95 # URI of the remote RESTful server
96 fun tnitter_server_uri
: String do return "http://localhost:8080"
98 # `AsyncHttpRequest` with services to act on the windows of the app
99 abstract class AsyncTnitterRequest
100 super AsyncHttpRequest
102 private var window
: TnitterWindow
104 redef fun rest_server_uri
do return tnitter_server_uri
106 redef var rest_action
108 # Should this request be delayed by `request_delay_on_error` seconds?
113 if delay
then nanosleep
(request_delay_on_error
, 0)
118 # Async request to list latest posts, either immediately or by push notification
120 # Implementation note:
121 # This class could as well be merged with `AsyncTnitterRequest` or have two versions,
122 # one for the immediate update and one for the push notification.
123 # We chose this structure for simplicity of the example,
124 # and as more services may be added in the future.
125 # If these future services expect data of a different format,
126 # they will need a different `on_load` but could still use `AsyncTnitterRequest`.
127 class ListPostRequest
128 super AsyncTnitterRequest
130 redef fun on_load
(posts
)
132 # Deal with server-side errors
133 if posts
isa Error then
134 print_error
"Server Error: '{posts.message}' from '{rest_server_uri / rest_action}'"
139 if not posts
isa Array[Post] then
140 print_error
"Error: Got '{posts or else "null"}'"
144 # Update UI and prepare for the next update
145 window
.apply_update posts
146 window
.request_push_notification
149 redef fun on_fail
(error
)
151 print
"Warning: Request {rest_server_uri/rest_action} failed with {error}"
152 window
.request_full_list_on_error
159 redef class Deserializer
160 redef fun deserialize_class
(name
)
162 # This is usually generated using `nitserial`,
163 # but for a single generic class it is easier to implement manually
165 if name
== "Array[Post]" then return new Array[Post].from_deserializer
(self)