diff --git a/clients/android/NewsBlur/res/layout/include_comment.xml b/clients/android/NewsBlur/res/layout/include_comment.xml index f5b0b214c..9c6196938 100644 --- a/clients/android/NewsBlur/res/layout/include_comment.xml +++ b/clients/android/NewsBlur/res/layout/include_comment.xml @@ -133,15 +133,15 @@ - - \ No newline at end of file + diff --git a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java index 33949f10e..7818bcee2 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/BlurDatabaseHelper.java @@ -323,6 +323,7 @@ public class BlurDatabaseHelper { commentValues.add(comment.getValues()); for (Reply reply : comment.replies) { reply.commentId = comment.id; + reply.id = reply.constructId(); replyValues.add(reply.getValues()); } } @@ -334,6 +335,7 @@ public class BlurDatabaseHelper { commentValues.add(comment.getValues()); for (Reply reply : comment.replies) { reply.commentId = comment.id; + reply.id = reply.constructId(); replyValues.add(reply.getValues()); } } @@ -346,6 +348,7 @@ public class BlurDatabaseHelper { commentValues.add(comment.getValues()); for (Reply reply : comment.replies) { reply.commentId = comment.id; + reply.id = reply.constructId(); replyValues.add(reply.getValues()); } } @@ -965,7 +968,7 @@ public class BlurDatabaseHelper { public List getCommentReplies(String commentId) { String[] selArgs = new String[] {commentId}; String selection = DatabaseConstants.REPLY_COMMENTID+ " = ?"; - Cursor c = dbRO.query(DatabaseConstants.REPLY_TABLE, DatabaseConstants.REPLY_COLUMNS, selection, selArgs, null, null, DatabaseConstants.REPLY_DATE + " DESC"); + Cursor c = dbRO.query(DatabaseConstants.REPLY_TABLE, null, selection, selArgs, null, null, DatabaseConstants.REPLY_DATE + " ASC"); List replies = new ArrayList(c.getCount()); while (c.moveToNext()) { replies.add(Reply.fromCursor(c)); @@ -974,6 +977,16 @@ public class BlurDatabaseHelper { return replies; } + public void replyToComment(String storyId, String feedId, String commentUserId, String replyText) { + Reply reply = new Reply(); + reply.commentId = Comment.constructId(storyId, feedId, commentUserId); + reply.text = replyText; + reply.userId = PrefsUtils.getUserDetails(context).id; + reply.date = new Date(); + reply.id = reply.constructId(); + synchronized (RW_MUTEX) {dbRW.insertWithOnConflict(DatabaseConstants.REPLY_TABLE, null, reply.getValues(), SQLiteDatabase.CONFLICT_REPLACE);} + } + public static void closeQuietly(Cursor c) { if (c == null) return; try {c.close();} catch (Exception e) {;} diff --git a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java index 44ae2d24b..fb83268d1 100644 --- a/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java +++ b/clients/android/NewsBlur/src/com/newsblur/database/DatabaseConstants.java @@ -134,7 +134,8 @@ public class DatabaseConstants { public static final String ACTION_UNSHARE = "unshare"; public static final String ACTION_LIKE_COMMENT = "like_comment"; public static final String ACTION_UNLIKE_COMMENT = "unlike_comment"; - public static final String ACTION_COMMENT = "comment"; + public static final String ACTION_REPLY = "reply"; + public static final String ACTION_COMMENT_TEXT = "comment_text"; public static final String ACTION_STORY_HASH = "story_hash"; public static final String ACTION_FEED_ID = "feed_id"; public static final String ACTION_INCLUDE_OLDER = "include_older"; @@ -275,7 +276,8 @@ public class DatabaseConstants { ACTION_UNSHARE + INTEGER + " DEFAULT 0, " + ACTION_LIKE_COMMENT + INTEGER + " DEFAULT 0, " + ACTION_UNLIKE_COMMENT + INTEGER + " DEFAULT 0, " + - ACTION_COMMENT + TEXT + ", " + + ACTION_REPLY + INTEGER + " DEFAULT 0, " + + ACTION_COMMENT_TEXT + TEXT + ", " + ACTION_STORY_HASH + TEXT + ", " + ACTION_FEED_ID + TEXT + ", " + ACTION_INCLUDE_OLDER + INTEGER + ", " + @@ -294,10 +296,6 @@ public class DatabaseConstants { SOCIAL_FEED_ID, SOCIAL_FEED_USERNAME, SOCIAL_FEED_TITLE, SOCIAL_FEED_ICON, SOCIAL_FEED_POSITIVE_COUNT, SOCIAL_FEED_NEUTRAL_COUNT, SOCIAL_FEED_NEGATIVE_COUNT }; - public static final String[] REPLY_COLUMNS = { - REPLY_COMMENTID, REPLY_DATE, REPLY_ID, REPLY_SHORTDATE, REPLY_TEXT, REPLY_USERID - }; - public static final String SUM_STORY_TOTAL = "storyTotal"; private static String STORY_SUM_TOTAL = " CASE " + "WHEN MAX(" + STORY_INTELLIGENCE_AUTHORS + "," + STORY_INTELLIGENCE_TAGS + "," + STORY_INTELLIGENCE_TITLE + ") > 0 " + diff --git a/clients/android/NewsBlur/src/com/newsblur/domain/Comment.java b/clients/android/NewsBlur/src/com/newsblur/domain/Comment.java index 89ad3bea7..86653cd44 100644 --- a/clients/android/NewsBlur/src/com/newsblur/domain/Comment.java +++ b/clients/android/NewsBlur/src/com/newsblur/domain/Comment.java @@ -76,7 +76,7 @@ public class Comment implements Serializable { } public static String constructId(String storyId, String feedId, String userId) { - return TextUtils.concat(storyId, feedId, userId).toString(); + return TextUtils.concat(feedId, storyId, userId).toString(); } } diff --git a/clients/android/NewsBlur/src/com/newsblur/domain/Reply.java b/clients/android/NewsBlur/src/com/newsblur/domain/Reply.java index b3e9fca9f..a95ccbe4c 100644 --- a/clients/android/NewsBlur/src/com/newsblur/domain/Reply.java +++ b/clients/android/NewsBlur/src/com/newsblur/domain/Reply.java @@ -4,6 +4,7 @@ import java.util.Date; import android.content.ContentValues; import android.database.Cursor; +import android.text.TextUtils; import com.google.gson.annotations.SerializedName; import com.newsblur.database.DatabaseConstants; @@ -24,6 +25,7 @@ public class Reply { @SerializedName("date") public Date date; + // NB: this is the commentId that we generate, not the API one public String commentId; public ContentValues getValues() { @@ -47,4 +49,9 @@ public class Reply { reply.userId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.REPLY_USERID)); return reply; } -} \ No newline at end of file + + // construct a string we can internally use as a PK + public String constructId() { + return TextUtils.concat(commentId, "_", Long.toString(date.getTime())).toString(); + } +} diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/ReplyDialogFragment.java b/clients/android/NewsBlur/src/com/newsblur/fragment/ReplyDialogFragment.java index ff9d257db..0f4e06e6e 100644 --- a/clients/android/NewsBlur/src/com/newsblur/fragment/ReplyDialogFragment.java +++ b/clients/android/NewsBlur/src/com/newsblur/fragment/ReplyDialogFragment.java @@ -15,6 +15,7 @@ import android.widget.Toast; import com.newsblur.R; import com.newsblur.domain.Story; import com.newsblur.network.APIManager; +import com.newsblur.util.FeedUtils; public class ReplyDialogFragment extends DialogFragment { @@ -25,9 +26,6 @@ public class ReplyDialogFragment extends DialogFragment { private String commentUserId, commentUsername; private Story story; - private APIManager apiManager; - - public static ReplyDialogFragment newInstance(final Story story, final String commentUserId, final String commentUsername) { ReplyDialogFragment frag = new ReplyDialogFragment(); Bundle args = new Bundle(); @@ -40,17 +38,14 @@ public class ReplyDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - story = (Story) getArguments().getSerializable(STORY); - commentUserId = getArguments().getString(COMMENT_USER_ID); commentUsername = getArguments().getString(COMMENT_USERNAME); final Activity activity = getActivity(); - apiManager = new APIManager(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity); - final String shareString = getResources().getString(R.string.reply_to); + String shareString = getResources().getString(R.string.reply_to); builder.setTitle(String.format(shareString, getArguments().getString(COMMENT_USERNAME))); LayoutInflater layoutInflater = LayoutInflater.from(activity); @@ -61,23 +56,7 @@ public class ReplyDialogFragment extends DialogFragment { builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - - new AsyncTask() { - @Override - protected Boolean doInBackground(Void... arg) { - return apiManager.replyToComment(story.id, story.feedId, commentUserId, reply.getText().toString()); - } - - @Override - protected void onPostExecute(Boolean result) { - if (result) { - Toast.makeText(activity, R.string.replied, Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(activity, R.string.error_replying, Toast.LENGTH_LONG).show(); - } - ReplyDialogFragment.this.dismiss(); - }; - }.execute(); + FeedUtils.replyToComment(story.id, story.feedId, commentUserId, reply.getText().toString(), activity); } }); builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() { diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/SetupCommentSectionTask.java b/clients/android/NewsBlur/src/com/newsblur/fragment/SetupCommentSectionTask.java index 441a0b30e..5e7b341ab 100644 --- a/clients/android/NewsBlur/src/com/newsblur/fragment/SetupCommentSectionTask.java +++ b/clients/android/NewsBlur/src/com/newsblur/fragment/SetupCommentSectionTask.java @@ -177,8 +177,10 @@ public class SetupCommentSectionTask extends AsyncTask { replyUsername.setText(R.string.unknown_user); } - TextView replySharedDate = (TextView) replyView.findViewById(R.id.reply_shareddate); - replySharedDate.setText(reply.shortDate + " ago"); + if (reply.shortDate != null) { + TextView replySharedDate = (TextView) replyView.findViewById(R.id.reply_shareddate); + replySharedDate.setText(reply.shortDate + " ago"); + } ((LinearLayout) commentView.findViewById(R.id.comment_replies_container)).addView(replyView); } diff --git a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java index 20cfc96dc..b0e455bf6 100644 --- a/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java +++ b/clients/android/NewsBlur/src/com/newsblur/network/APIManager.java @@ -466,14 +466,14 @@ public class APIManager { return response.getResponse(gson, NewsBlurResponse.class); } - public boolean replyToComment(String storyId, String storyFeedId, String commentUserId, String reply) { + public NewsBlurResponse replyToComment(String storyId, String storyFeedId, String commentUserId, String reply) { ContentValues values = new ContentValues(); values.put(APIConstants.PARAMETER_STORYID, storyId); values.put(APIConstants.PARAMETER_STORY_FEEDID, storyFeedId); values.put(APIConstants.PARAMETER_COMMENT_USERID, commentUserId); values.put(APIConstants.PARAMETER_REPLY_TEXT, reply); - final APIResponse response = post(APIConstants.URL_REPLY_TO, values); - return (!response.isError()); + APIResponse response = post(APIConstants.URL_REPLY_TO, values); + return response.getResponse(gson, NewsBlurResponse.class); } public boolean addFeed(String feedUrl, String folderName) { diff --git a/clients/android/NewsBlur/src/com/newsblur/serialization/DateStringTypeAdapter.java b/clients/android/NewsBlur/src/com/newsblur/serialization/DateStringTypeAdapter.java index 9fb08cbb1..ed5333883 100644 --- a/clients/android/NewsBlur/src/com/newsblur/serialization/DateStringTypeAdapter.java +++ b/clients/android/NewsBlur/src/com/newsblur/serialization/DateStringTypeAdapter.java @@ -5,6 +5,7 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.TimeZone; import android.text.TextUtils; import android.util.Log; @@ -15,8 +16,15 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParseException; public class DateStringTypeAdapter implements JsonDeserializer { - // 2012-07-23 02:43:02 - private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + private final DateFormat df; + + public DateStringTypeAdapter() { + // API sends dates like 2012-07-23 02:43:02 + df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + // API doesn't indicate TZ, but it is UTC + df.setTimeZone(TimeZone.getTimeZone("UTC")); + } @Override public Date deserialize(JsonElement element, Type type, JsonDeserializationContext arg2) throws JsonParseException { diff --git a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java index a80c198c3..d5ba0e972 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/FeedUtils.java @@ -227,6 +227,14 @@ public class FeedUtils { NbActivity.updateAllActivities(true); triggerSync(context); } + + public static void replyToComment(String storyId, String feedId, String commentUserId, String replyText, Context context) { + ReadingAction ra = ReadingAction.replyToComment(storyId, feedId, commentUserId, replyText); + dbHelper.enqueueAction(ra); + ra.doLocal(dbHelper); + NbActivity.updateAllActivities(true); + triggerSync(context); + } public static FeedSet feedSetFromFolderName(String folderName) { return FeedSet.folder(folderName, getFeedIdsRecursive(folderName)); diff --git a/clients/android/NewsBlur/src/com/newsblur/util/ReadingAction.java b/clients/android/NewsBlur/src/com/newsblur/util/ReadingAction.java index b4c875233..d371fe16d 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/ReadingAction.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/ReadingAction.java @@ -33,7 +33,7 @@ public class ReadingAction { private String storyId; private String feedId; private String sourceUserId; - private String commentText; + private String commentReplyText; // used for both comments and replies private String commentUserId; private ReadingAction() { @@ -77,14 +77,14 @@ public class ReadingAction { return ra; } - public static ReadingAction shareStory(String hash, String storyId, String feedId, String sourceUserId, String commentText) { + public static ReadingAction shareStory(String hash, String storyId, String feedId, String sourceUserId, String commentReplyText) { ReadingAction ra = new ReadingAction(); ra.type = ActionType.SHARE; ra.storyHash = hash; ra.storyId = storyId; ra.feedId = feedId; ra.sourceUserId = sourceUserId; - ra.commentText = commentText; + ra.commentReplyText = commentReplyText; return ra; } @@ -106,6 +106,16 @@ public class ReadingAction { return ra; } + public static ReadingAction replyToComment(String storyId, String feedId, String commentUserId, String commentReplyText) { + ReadingAction ra = new ReadingAction(); + ra.type = ActionType.REPLY; + ra.storyId = storyId; + ra.commentUserId = commentUserId; + ra.feedId = feedId; + ra.commentReplyText = commentReplyText; + return ra; + } + public ContentValues toContentValues() { ContentValues values = new ContentValues(); values.put(DatabaseConstants.ACTION_TIME, System.currentTimeMillis()); @@ -145,7 +155,7 @@ public class ReadingAction { values.put(DatabaseConstants.ACTION_STORY_ID, storyId); values.put(DatabaseConstants.ACTION_FEED_ID, feedId); values.put(DatabaseConstants.ACTION_SOURCE_USER_ID, sourceUserId); - values.put(DatabaseConstants.ACTION_COMMENT, commentText); + values.put(DatabaseConstants.ACTION_COMMENT_TEXT, commentReplyText); break; case LIKE_COMMENT: @@ -162,6 +172,14 @@ public class ReadingAction { values.put(DatabaseConstants.ACTION_COMMENT_ID, commentUserId); break; + case REPLY: + values.put(DatabaseConstants.ACTION_REPLY, 1); + values.put(DatabaseConstants.ACTION_STORY_ID, storyId); + values.put(DatabaseConstants.ACTION_FEED_ID, feedId); + values.put(DatabaseConstants.ACTION_COMMENT_ID, commentUserId); + values.put(DatabaseConstants.ACTION_COMMENT_TEXT, commentReplyText); + break; + default: throw new IllegalStateException("cannot serialise uknown type of action."); @@ -202,7 +220,7 @@ public class ReadingAction { ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID)); ra.feedId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID)); ra.sourceUserId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_SOURCE_USER_ID)); - ra.commentText = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT)); + ra.commentReplyText = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_TEXT)); } else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_LIKE_COMMENT)) == 1) { ra.type = ActionType.LIKE_COMMENT; ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID)); @@ -213,6 +231,12 @@ public class ReadingAction { ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID)); ra.feedId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID)); ra.commentUserId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_ID)); + } else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_REPLY)) == 1) { + ra.type = ActionType.REPLY; + ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID)); + ra.feedId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID)); + ra.commentUserId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_ID)); + ra.commentReplyText = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_TEXT)); } else { throw new IllegalStateException("cannot deserialise uknown type of action."); } @@ -243,7 +267,7 @@ public class ReadingAction { return apiManager.markStoryAsUnstarred(storyHash); case SHARE: - return apiManager.shareStory(storyId, feedId, commentText, sourceUserId); + return apiManager.shareStory(storyId, feedId, commentReplyText, sourceUserId); case LIKE_COMMENT: return apiManager.favouriteComment(storyId, commentUserId, feedId); @@ -251,6 +275,9 @@ public class ReadingAction { case UNLIKE_COMMENT: return apiManager.unFavouriteComment(storyId, commentUserId, feedId); + case REPLY: + return apiManager.replyToComment(storyId, feedId, commentUserId, commentReplyText); + default: } @@ -286,8 +313,8 @@ public class ReadingAction { case SHARE: dbHelper.setStoryShared(storyHash); - if (!TextUtils.isEmpty(commentText)) { - dbHelper.insertUpdateComment(storyId, feedId, commentText); + if (!TextUtils.isEmpty(commentReplyText)) { + dbHelper.insertUpdateComment(storyId, feedId, commentReplyText); } break; @@ -299,6 +326,10 @@ public class ReadingAction { dbHelper.setCommentLiked(storyId, commentUserId, feedId, false); break; + case REPLY: + dbHelper.replyToComment(storyId, feedId, commentUserId, commentReplyText); + break; + default: // not all actions have these, which is fine }