Skip to content

Commit bf3b212

Browse files
authored
Merge pull request #15570 from ovitrif/issue/13292-copy-published-post-url
Posts List: Add Copy link functionality for posts
2 parents 822d031 + ca240a3 commit bf3b212

File tree

11 files changed

+184
-60
lines changed

11 files changed

+184
-60
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
18.8
44
-----
5+
* [**] Posts List: Adds copy post link functionality [https://github.com/wordpress-mobile/WordPress-Android/pull/15570]
56
* [*] Editor: Replace snackbar with compact notice when switching between HTML or Visual mode [https://github.com/wordpress-mobile/WordPress-Android/pull/15583]
67
* [*] Fixed an issue where multiple screens were rendered incorrectly in RTL interface languages [https://github.com/wordpress-mobile/WordPress-Android/pull/15597]
78

WordPress/src/main/java/org/wordpress/android/ui/posts/PostActionHandler.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.wordpress.android.ui.posts
22

33
import android.content.Intent
4+
import com.google.android.material.snackbar.Snackbar
45
import org.wordpress.android.R
56
import org.wordpress.android.fluxc.Dispatcher
67
import org.wordpress.android.fluxc.generated.PostActionBuilder
@@ -29,12 +30,12 @@ import org.wordpress.android.ui.posts.RemotePreviewLogicHelper.RemotePreviewType
2930
import org.wordpress.android.ui.uploads.UploadService
3031
import org.wordpress.android.ui.uploads.UploadUtils
3132
import org.wordpress.android.ui.utils.UiString.UiStringRes
32-
import org.wordpress.android.util.ToastUtils
3333
import org.wordpress.android.util.ToastUtils.Duration
3434
import org.wordpress.android.viewmodel.helpers.ToastMessageHolder
3535
import org.wordpress.android.widgets.PostListButtonType
3636
import org.wordpress.android.widgets.PostListButtonType.BUTTON_CANCEL_PENDING_AUTO_UPLOAD
3737
import org.wordpress.android.widgets.PostListButtonType.BUTTON_COPY
38+
import org.wordpress.android.widgets.PostListButtonType.BUTTON_COPY_URL
3839
import org.wordpress.android.widgets.PostListButtonType.BUTTON_DELETE
3940
import org.wordpress.android.widgets.PostListButtonType.BUTTON_DELETE_PERMANENTLY
4041
import org.wordpress.android.widgets.PostListButtonType.BUTTON_EDIT
@@ -98,7 +99,7 @@ class PostActionHandler(
9899
showToast = showToast,
99100
messageMediaUploading = ToastMessageHolder(
100101
R.string.editor_toast_uploading_please_wait,
101-
ToastUtils.Duration.SHORT
102+
Duration.SHORT
102103
)
103104
)
104105
)
@@ -115,6 +116,7 @@ class PostActionHandler(
115116
}
116117
}
117118
BUTTON_COPY -> copyPost(site, post, true)
119+
BUTTON_COPY_URL -> triggerPostListAction.invoke(copyUrlAction(post))
118120
BUTTON_DELETE, BUTTON_DELETE_PERMANENTLY -> {
119121
postListDialogHelper.showDeletePostConfirmationDialog(post)
120122
}
@@ -129,6 +131,22 @@ class PostActionHandler(
129131
}
130132
}
131133

134+
private fun copyUrlAction(post: PostModel) = PostListAction.CopyUrl(
135+
site = site,
136+
post = post,
137+
showSnackbar = showSnackbar,
138+
messageSuccess = SnackbarMessageHolder(
139+
UiStringRes(R.string.post_link_copied_to_clipboard),
140+
duration = Snackbar.LENGTH_SHORT,
141+
isImportant = false
142+
),
143+
messageError = SnackbarMessageHolder(
144+
UiStringRes(R.string.error_copy_to_clipboard),
145+
duration = Snackbar.LENGTH_SHORT,
146+
isImportant = false
147+
)
148+
)
149+
132150
private fun cancelPendingAutoUpload(post: PostModel) {
133151
val msgRes = UploadUtils.cancelPendingAutoUpload(post, dispatcher)
134152
showSnackbar.invoke(SnackbarMessageHolder(UiStringRes(msgRes)))

WordPress/src/main/java/org/wordpress/android/ui/posts/PostListAction.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package org.wordpress.android.ui.posts
22

3+
import android.content.ClipData
34
import androidx.fragment.app.FragmentActivity
45
import org.wordpress.android.fluxc.model.PostModel
56
import org.wordpress.android.fluxc.model.SiteModel
67
import org.wordpress.android.push.NativeNotificationsUtils
78
import org.wordpress.android.ui.ActivityLauncher
89
import org.wordpress.android.ui.PagePostCreationSourcesDetail.POST_FROM_POSTS_LIST
10+
import org.wordpress.android.ui.pages.SnackbarMessageHolder
911
import org.wordpress.android.ui.photopicker.MediaPickerLauncher
1012
import org.wordpress.android.ui.posts.RemotePreviewLogicHelper.RemotePreviewType
1113
import org.wordpress.android.ui.prefs.AppPrefs
1214
import org.wordpress.android.ui.stories.intro.StoriesIntroDialogFragment
1315
import org.wordpress.android.ui.uploads.UploadService
16+
import org.wordpress.android.util.AppLog
17+
import org.wordpress.android.util.clipboardManager
1418
import org.wordpress.android.viewmodel.helpers.ToastMessageHolder
1519

1620
sealed class PostListAction {
@@ -33,6 +37,13 @@ sealed class PostListAction {
3337
val post: PostModel,
3438
val trackAnalytics: Boolean = PostUtils.isFirstTimePublish(post)
3539
) : PostListAction()
40+
class CopyUrl(
41+
val site: SiteModel,
42+
val post: PostModel,
43+
val showSnackbar: (SnackbarMessageHolder) -> Unit,
44+
val messageSuccess: SnackbarMessageHolder,
45+
val messageError: SnackbarMessageHolder
46+
) : PostListAction()
3647

3748
class ViewStats(val site: SiteModel, val post: PostModel) : PostListAction()
3849
class ViewPost(val site: SiteModel, val post: PostModel) : PostListAction()
@@ -91,5 +102,21 @@ fun handlePostListAction(
91102
is PostListAction.DismissPendingNotification -> {
92103
NativeNotificationsUtils.dismissNotification(action.pushId, activity)
93104
}
105+
is PostListAction.CopyUrl -> {
106+
try {
107+
activity.clipboardManager?.setPrimaryClip(
108+
ClipData.newPlainText("${action.post.id}", action.post.link)
109+
) ?: throw NullPointerException("ClipboardManager is not supported on this device")
110+
111+
action.showSnackbar.invoke(action.messageSuccess)
112+
} catch (e: Exception) {
113+
/**
114+
* Ignore any exceptions here as certain devices have bugs and will fail.
115+
* See https://crrev.com/542cb9cfcc927295615809b0c99917b09a219d9f for more info.
116+
*/
117+
AppLog.e(AppLog.T.POSTS, e)
118+
action.showSnackbar.invoke(action.messageError)
119+
}
120+
}
94121
}
95122
}

WordPress/src/main/java/org/wordpress/android/ui/posts/PostListActionTracker.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.wordpress.android.util.analytics.AnalyticsUtils
77
import org.wordpress.android.widgets.PostListButtonType
88
import org.wordpress.android.widgets.PostListButtonType.BUTTON_CANCEL_PENDING_AUTO_UPLOAD
99
import org.wordpress.android.widgets.PostListButtonType.BUTTON_COPY
10+
import org.wordpress.android.widgets.PostListButtonType.BUTTON_COPY_URL
1011
import org.wordpress.android.widgets.PostListButtonType.BUTTON_DELETE
1112
import org.wordpress.android.widgets.PostListButtonType.BUTTON_DELETE_PERMANENTLY
1213
import org.wordpress.android.widgets.PostListButtonType.BUTTON_EDIT
@@ -49,6 +50,7 @@ fun trackPostListAction(site: SiteModel, buttonType: PostListButtonType, postDat
4950
BUTTON_MOVE_TO_DRAFT -> "move_to_draft"
5051
BUTTON_CANCEL_PENDING_AUTO_UPLOAD -> "cancel_pending_auto_upload"
5152
BUTTON_SHOW_MOVE_TRASHED_POST_TO_DRAFT_DIALOG -> "show_move_trashed_post_to_draft_post_dialog"
53+
BUTTON_COPY_URL -> "copy_url"
5254
}
5355

5456
AnalyticsUtils.trackWithSiteDetails(statsEvent, site, properties)

WordPress/src/main/java/org/wordpress/android/util/ContextExtensions.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.wordpress.android.util
22

3+
import android.content.ClipboardManager
34
import android.content.Context
45
import android.content.res.ColorStateList
56
import android.util.TypedValue
@@ -41,3 +42,10 @@ fun Context.getColorStateListFromAttribute(@AttrRes attribute: Int): ColorStateL
4142
// https://developer.android.com/reference/android/content/res/Configuration.html#locale
4243
val Context.currentLocale: Locale
4344
get() = ConfigurationCompat.getLocales(resources.configuration)[0]
45+
46+
/**
47+
* Gets the clipboard manager system service
48+
* @see android.content.ClipboardManager
49+
*/
50+
val Context.clipboardManager: ClipboardManager?
51+
get() = ContextCompat.getSystemService(this, ClipboardManager::class.java)

WordPress/src/main/java/org/wordpress/android/viewmodel/posts/PostListItemUiStateHelper.kt

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import org.wordpress.android.viewmodel.uistate.ProgressBarUiState
4646
import org.wordpress.android.widgets.PostListButtonType
4747
import org.wordpress.android.widgets.PostListButtonType.BUTTON_CANCEL_PENDING_AUTO_UPLOAD
4848
import org.wordpress.android.widgets.PostListButtonType.BUTTON_COPY
49+
import org.wordpress.android.widgets.PostListButtonType.BUTTON_COPY_URL
4950
import org.wordpress.android.widgets.PostListButtonType.BUTTON_DELETE
5051
import org.wordpress.android.widgets.PostListButtonType.BUTTON_DELETE_PERMANENTLY
5152
import org.wordpress.android.widgets.PostListButtonType.BUTTON_EDIT
@@ -121,7 +122,7 @@ class PostListItemUiStateHelper @Inject constructor(
121122
hasAutoSave = hasAutoSave
122123
)
123124
val statusesColor = labelColorUseCase.getLabelsColor(post, uploadUiState, unhandledConflicts, hasAutoSave)
124-
val statusesDelimeter = UiStringRes(R.string.multiple_status_label_delimiter)
125+
val statusesDelimiter = UiStringRes(R.string.multiple_status_label_delimiter)
125126
val onSelected = {
126127
when (postStatus) {
127128
TRASHED -> {
@@ -147,7 +148,7 @@ class PostListItemUiStateHelper @Inject constructor(
147148
postInfo = postInfo,
148149
statuses = statuses,
149150
statusesColor = statusesColor,
150-
statusesDelimiter = statusesDelimeter,
151+
statusesDelimiter = statusesDelimiter,
151152
progressBarUiState = getProgressBarState(
152153
uploadUiState = uploadUiState,
153154
performingCriticalAction = performingCriticalAction
@@ -156,7 +157,7 @@ class PostListItemUiStateHelper @Inject constructor(
156157
uploadUiState = uploadUiState,
157158
performingCriticalAction = performingCriticalAction
158159
),
159-
disableRippleEffect = postStatus == PostStatus.TRASHED
160+
disableRippleEffect = postStatus == TRASHED
160161
)
161162

162163
return PostListItemUiState(
@@ -368,9 +369,9 @@ class PostListItemUiStateHelper @Inject constructor(
368369
siteHasCapabilitiesToPublish: Boolean,
369370
statsSupported: Boolean
370371
): List<PostListButtonType> {
371-
val canRetryUpload = uploadUiState is PostUploadUiState.UploadFailed
372+
val canRetryUpload = uploadUiState is UploadFailed
372373
val canCancelPendingAutoUpload = (uploadUiState is UploadWaitingForConnection ||
373-
(uploadUiState is PostUploadUiState.UploadFailed && uploadUiState.isEligibleForAutoUpload))
374+
(uploadUiState is UploadFailed && uploadUiState.isEligibleForAutoUpload))
374375
val canPublishPost = (canRetryUpload || uploadUiState is NothingToUpload || !canCancelPendingAutoUpload) &&
375376
(isLocallyChanged || isLocalDraft || postStatus == DRAFT ||
376377
(siteHasCapabilitiesToPublish && postStatus == PENDING))
@@ -380,7 +381,8 @@ class PostListItemUiStateHelper @Inject constructor(
380381
!isLocalDraft &&
381382
!isLocallyChanged
382383
val canShowCopy = postStatus == PUBLISHED || postStatus == DRAFT
383-
val canShowViewButton = !canRetryUpload && postStatus != PostStatus.TRASHED
384+
val canShowCopyUrlButton = !isLocalDraft && postStatus != TRASHED
385+
val canShowViewButton = !canRetryUpload && postStatus != TRASHED
384386
val canShowPublishButton = canRetryUpload || canPublishPost
385387
val buttonTypes = ArrayList<PostListButtonType>()
386388

@@ -394,26 +396,17 @@ class PostListItemUiStateHelper @Inject constructor(
394396

395397
if (canShowPublishButton) {
396398
buttonTypes.add(
397-
if (canRetryUpload) {
398-
BUTTON_RETRY
399-
} else if (!siteHasCapabilitiesToPublish) {
400-
BUTTON_SUBMIT
401-
} else if (postStatus == SCHEDULED && isLocallyChanged) {
402-
BUTTON_SYNC
403-
} else {
404-
BUTTON_PUBLISH
399+
when {
400+
canRetryUpload -> BUTTON_RETRY
401+
!siteHasCapabilitiesToPublish -> BUTTON_SUBMIT
402+
postStatus == SCHEDULED && isLocallyChanged -> BUTTON_SYNC
403+
else -> BUTTON_PUBLISH
405404
}
406405
)
407406
}
408407

409408
if (canShowViewButton) {
410-
buttonTypes.add(
411-
if (isLocalDraft || isLocallyChanged) {
412-
BUTTON_PREVIEW
413-
} else {
414-
BUTTON_VIEW
415-
}
416-
)
409+
buttonTypes.addViewOrPreviewAction(isLocalDraft || isLocallyChanged)
417410
}
418411

419412
if (canShowStats) {
@@ -426,17 +419,30 @@ class PostListItemUiStateHelper @Inject constructor(
426419

427420
buttonTypes.addMoveToDraftActionIfAvailable(postStatus)
428421

429-
when {
430-
isLocalDraft -> buttonTypes.add(BUTTON_DELETE)
431-
postStatus == TRASHED -> {
432-
buttonTypes.add(BUTTON_DELETE_PERMANENTLY)
433-
}
434-
postStatus != TRASHED -> buttonTypes.add(BUTTON_TRASH)
422+
if (canShowCopyUrlButton) {
423+
buttonTypes.add(BUTTON_COPY_URL)
435424
}
436425

426+
buttonTypes.addDeletingOrTrashAction(isLocalDraft, postStatus)
427+
437428
return buttonTypes
438429
}
439430

431+
private fun MutableList<PostListButtonType>.addViewOrPreviewAction(shouldShowPreview: Boolean) {
432+
add(if (shouldShowPreview) BUTTON_PREVIEW else BUTTON_VIEW)
433+
}
434+
435+
private fun MutableList<PostListButtonType>.addDeletingOrTrashAction(
436+
isLocalDraft: Boolean,
437+
postStatus: PostStatus
438+
) {
439+
when {
440+
isLocalDraft -> add(BUTTON_DELETE)
441+
postStatus == TRASHED -> add(BUTTON_DELETE_PERMANENTLY)
442+
postStatus != TRASHED -> add(BUTTON_TRASH)
443+
}
444+
}
445+
440446
private fun MutableList<PostListButtonType>.addMoveToDraftActionIfAvailable(postStatus: PostStatus) {
441447
val canMovePostToDraft = postStatus == PUBLISHED || postStatus == TRASHED
442448

WordPress/src/main/java/org/wordpress/android/widgets/PostListButtonType.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ enum class PostListButtonType constructor(
3737
R.attr.wpColorWarningDark
3838
),
3939
BUTTON_SHOW_MOVE_TRASHED_POST_TO_DRAFT_DIALOG(15, 0, 0, 0),
40-
BUTTON_COPY(16, R.string.button_copy, R.drawable.ic_copy_white_24dp, R.attr.colorOnSurface);
40+
BUTTON_COPY(16, R.string.button_copy, R.drawable.ic_copy_white_24dp, R.attr.colorOnSurface),
41+
BUTTON_COPY_URL(17, R.string.button_copy_link, R.drawable.ic_gridicons_link_white_24dp, R.attr.colorOnSurface);
4142

4243
companion object {
4344
fun fromInt(value: Int): PostListButtonType? = values().firstOrNull { it.value == value }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportHeight="24"
5+
android:viewportWidth="24">
6+
<path
7+
android:fillColor="@android:color/white"
8+
android:pathData="M17.001,12.999H7.001V10.999H17.001V12.999ZM18.001,6.999H17.001C15.37,6.999 13.936,7.791 13.024,8.999H18.001C19.104,8.999 20.001,9.896 20.001,10.999V12.999C20.001,14.102 19.104,14.999 18.001,14.999H13.024C13.937,16.207 15.371,16.999 17.001,16.999H18.001C19.0618,16.999 20.0793,16.5776 20.8294,15.8275C21.5795,15.0773 22.001,14.0599 22.001,12.999V10.999C22.001,9.9382 21.5795,8.9207 20.8294,8.1706C20.0793,7.4205 19.0618,6.999 18.001,6.999ZM2.001,10.999V12.999C2.001,14.0599 2.4224,15.0773 3.1725,15.8275C3.9227,16.5776 4.9401,16.999 6.001,16.999H7.001C8.631,16.999 10.066,16.207 10.978,14.999H6.001C4.898,14.999 4.001,14.102 4.001,12.999V10.999C4.001,9.896 4.898,8.999 6.001,8.999H10.978C10.066,7.791 8.632,6.999 7.001,6.999H6.001C4.9401,6.999 3.9227,7.4205 3.1725,8.1706C2.4224,8.9207 2.001,9.9382 2.001,10.999V10.999Z" />
9+
</vector>

WordPress/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@
309309
<string name="button_retry">Retry</string>
310310
<string name="button_move_to_draft">Move to Draft</string>
311311
<string name="button_copy">Duplicate</string>
312+
<string name="button_copy_link">Copy link</string>
312313

313314
<!-- post uploads -->
314315
<string name="upload_failed_param">Upload failed for \"%s\"</string>
@@ -504,6 +505,7 @@
504505
<string name="remove_account">Remove site</string>
505506

506507
<!-- post actions -->
508+
<string name="post_link_copied_to_clipboard">Link copied to clipboard</string>
507509
<string name="post_trashed">Post sent to trash</string>
508510
<string name="post_trashing">Post is being trashed</string>
509511
<string name="post_restored">Post restored</string>

0 commit comments

Comments
 (0)