Add relative and dynamic timestamps to the web interface.
[Sone.git] / src / main / java / net / pterodactylus / sone / web / ajax / GetTimesAjaxPage.java
1 /*
2  * Sone - GetTimesAjaxPage.java - Copyright © 2010–2011 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify it under
5  * the terms of the GNU General Public License as published by the Free Software
6  * Foundation, either version 3 of the License, or (at your option) any later
7  * version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  * details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.web.ajax;
19
20 import java.text.DateFormat;
21 import java.text.SimpleDateFormat;
22 import java.util.Date;
23
24 import net.pterodactylus.sone.data.Post;
25 import net.pterodactylus.sone.data.Reply;
26 import net.pterodactylus.sone.web.WebInterface;
27 import net.pterodactylus.util.json.JsonObject;
28 import net.pterodactylus.util.number.Digits;
29
30 /**
31  * Ajax page that returns a formatted, relative timestamp for replies or posts.
32  *
33  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
34  */
35 public class GetTimesAjaxPage extends JsonPage {
36
37         /** Formatter for tooltips. */
38         private static final DateFormat dateFormat = new SimpleDateFormat("MMM d, yyyy, HH:mm:ss");
39
40         /**
41          * Creates a new get times AJAX page.
42          *
43          * @param webInterface
44          *            The Sone web interface
45          */
46         public GetTimesAjaxPage(WebInterface webInterface) {
47                 super("getTimes.ajax", webInterface);
48         }
49
50         /**
51          * {@inheritDoc}
52          */
53         @Override
54         protected JsonObject createJsonObject(Request request) {
55                 long now = System.currentTimeMillis();
56                 String allIds = request.getHttpRequest().getParam("posts");
57                 JsonObject postTimes = new JsonObject();
58                 if (allIds.length() > 0) {
59                         String[] ids = allIds.split(",");
60                         for (String id : ids) {
61                                 Post post = webInterface.getCore().getPost(id, false);
62                                 if (post == null) {
63                                         continue;
64                                 }
65                                 long age = now - post.getTime();
66                                 JsonObject postTime = new JsonObject();
67                                 Time time = getTime(age);
68                                 postTime.put("timeText", time.getText());
69                                 postTime.put("refreshTime", time.getRefresh() / Time.SECOND);
70                                 postTime.put("tooltip", dateFormat.format(new Date(post.getTime())));
71                                 postTimes.put(id, postTime);
72                         }
73                 }
74                 JsonObject replyTimes = new JsonObject();
75                 allIds = request.getHttpRequest().getParam("replies");
76                 if (allIds.length() > 0) {
77                         String[] ids = allIds.split(",");
78                         for (String id : ids) {
79                                 Reply reply = webInterface.getCore().getReply(id, false);
80                                 if (reply == null) {
81                                         continue;
82                                 }
83                                 long age = now - reply.getTime();
84                                 JsonObject replyTime = new JsonObject();
85                                 Time time = getTime(age);
86                                 replyTime.put("timeText", time.getText());
87                                 replyTime.put("refreshTime", time.getRefresh() / Time.SECOND);
88                                 replyTime.put("tooltip", dateFormat.format(new Date(reply.getTime())));
89                                 replyTimes.put(id, replyTime);
90                         }
91                 }
92                 return createSuccessJsonObject().put("postTimes", postTimes).put("replyTimes", replyTimes);
93         }
94
95         /**
96          * {@inheritDoc}
97          */
98         @Override
99         protected boolean needsFormPassword() {
100                 return false;
101         }
102
103         /**
104          * {@inheritDoc}
105          */
106         @Override
107         protected boolean requiresLogin() {
108                 return false;
109         }
110
111         //
112         // PRIVATE METHODS
113         //
114
115         /**
116          * Returns the formatted relative time for a given age.
117          *
118          * @param age
119          *            The age to format (in milliseconds)
120          * @return The formatted age
121          */
122         private Time getTime(long age) {
123                 String text;
124                 long refresh;
125                 if (age < 0) {
126                         text = webInterface.getL10n().getDefaultString("View.Time.InTheFuture");
127                         refresh = 5 * Time.MINUTE;
128                 } else if (age < 30 * Time.SECOND) {
129                         text = webInterface.getL10n().getDefaultString("View.Time.AFewSecondsAgo");
130                         refresh = 5 * Time.SECOND;
131                 } else if (age < 1 * Time.MINUTE) {
132                         text = webInterface.getL10n().getString("View.Time.XSecondsAgo", "sec", String.valueOf((int) Digits.round(age / Time.SECOND, 10)));
133                         refresh = 10 * Time.SECOND;
134                 } else if (age < 2 * Time.MINUTE) {
135                         text = webInterface.getL10n().getString("View.Time.AMinuteAgo");
136                         refresh = Time.MINUTE;
137                 } else if (age < 30 * Time.MINUTE) {
138                         text = webInterface.getL10n().getString("View.Time.XMinutesAgo", "min", String.valueOf((int) Digits.round(age / Time.MINUTE, 1)));
139                         refresh = Time.MINUTE;
140                 } else if (age < 45 * Time.MINUTE) {
141                         text = webInterface.getL10n().getString("View.Time.HalfAnHourAgo");
142                         refresh = 10 * Time.MINUTE;
143                 } else if (age < 2 * Time.HOUR) {
144                         text = webInterface.getL10n().getString("View.Time.AnHourAgo");
145                         refresh = Time.HOUR;
146                 } else if (age < 21 * Time.HOUR) {
147                         text = webInterface.getL10n().getString("View.Time.XHoursAgo", "hour", String.valueOf((int) Digits.round(age / Time.HOUR, 1)));
148                         refresh = Time.HOUR;
149                 } else if (age < 42 * Time.HOUR) {
150                         text = webInterface.getL10n().getString("View.Time.ADayAgo");
151                         refresh = Time.DAY;
152                 } else if (age < 6 * Time.DAY) {
153                         text = webInterface.getL10n().getString("View.Time.XDaysAgo", "day", String.valueOf((int) Digits.round(age / Time.DAY, 1)));
154                         refresh = Time.DAY;
155                 } else if (age < 11 * Time.DAY) {
156                         text = webInterface.getL10n().getString("View.Time.AWeekAgo");
157                         refresh = Time.DAY;
158                 } else if (age < 4 * Time.WEEK) {
159                         text = webInterface.getL10n().getString("View.Time.XWeeksAgo", "week", String.valueOf((int) Digits.round(age / Time.WEEK, 1)));
160                         refresh = Time.DAY;
161                 } else if (age < 6 * Time.WEEK) {
162                         text = webInterface.getL10n().getString("View.Time.AMonthAgo");
163                         refresh = Time.DAY;
164                 } else if (age < 11 * Time.MONTH) {
165                         text = webInterface.getL10n().getString("View.Time.XMonthsAgo", "month", String.valueOf((int) Digits.round(age / Time.MONTH, 1)));
166                         refresh = Time.DAY;
167                 } else if (age < 18 * Time.MONTH) {
168                         text = webInterface.getL10n().getString("View.Time.AYearAgo");
169                         refresh = Time.WEEK;
170                 } else {
171                         text = webInterface.getL10n().getString("View.Time.XYearsAgo", "year", String.valueOf((int) Digits.round(age / Time.YEAR, 1)));
172                         refresh = Time.WEEK;
173                 }
174                 return new Time(text, refresh);
175         }
176
177         /**
178          * Container for a formatted time.
179          *
180          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
181          */
182         private static class Time {
183
184                 /** Number of milliseconds in a second. */
185                 private static final long SECOND = 1000;
186
187                 /** Number of milliseconds in a minute. */
188                 private static final long MINUTE = 60 * SECOND;
189
190                 /** Number of milliseconds in an hour. */
191                 private static final long HOUR = 60 * MINUTE;
192
193                 /** Number of milliseconds in a day. */
194                 private static final long DAY = 24 * HOUR;
195
196                 /** Number of milliseconds in a week. */
197                 private static final long WEEK = 7 * DAY;
198
199                 /** Number of milliseconds in a 30-day month. */
200                 private static final long MONTH = 30 * DAY;
201
202                 /** Number of milliseconds in a year. */
203                 private static final long YEAR = 365 * DAY;
204
205                 /** The formatted time. */
206                 private final String text;
207
208                 /** The time after which to refresh the time. */
209                 private final long refresh;
210
211                 /**
212                  * Creates a new formatted time container.
213                  *
214                  * @param text
215                  *            The formatted time
216                  * @param refresh
217                  *            The time after which to refresh the time (in milliseconds)
218                  */
219                 public Time(String text, long refresh) {
220                         this.text = text;
221                         this.refresh = refresh;
222                 }
223
224                 /**
225                  * Returns the formatted time.
226                  *
227                  * @return The formatted time
228                  */
229                 public String getText() {
230                         return text;
231                 }
232
233                 /**
234                  * Returns the time after which to refresh the time.
235                  *
236                  * @return The time after which to refresh the time (in milliseconds)
237                  */
238                 public long getRefresh() {
239                         return refresh;
240                 }
241
242         }
243
244 }