diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 34d7b40a88..501aa6784a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,6 +5,6 @@ - [ ] Changes has been tested on an Android device or Android emulator with API 21 - [ ] UI change has been tested on both light and dark themes - [ ] Pull request is based on the develop branch -- [ ] Pull request updates [CHANGES.md](https://github.com/vector-im/element-android/blob/develop/CHANGES.md) +- [ ] Pull request includes a new file under ./newsfragment. See https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#changelog - [ ] Pull request includes screenshots or videos if containing UI changes - [ ] Pull request includes a [sign off](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md#sign-off) diff --git a/CHANGES.md b/CHANGES.md index a467e043fd..b76b001268 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ Bugfix ๐Ÿ›: - Spaces | Personal spaces add DM - Web Parity (#3271) - Spaces | Improve 'Leave Space' UX/UI (#3359) - Don't create private spaces with encryption enabled (#3363) + - #+ button on lower right when looking at an empty space goes to an empty 'Explore rooms' (#3327) Translations ๐Ÿ—ฃ: - @@ -1382,36 +1383,3 @@ Changes in RiotX 0.1.0 (2019-07-11) First release! Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-android-b17952e8f771 - - -======================================================= -+ TEMPLATE WHEN PREPARING A NEW RELEASE + -======================================================= - - -Changes in Element 1.1.X (2021-XX-XX) -=================================================== - -Features โœจ: - - - -Improvements ๐Ÿ™Œ: - - - -Bugfix ๐Ÿ›: - - - -Translations ๐Ÿ—ฃ: - - - -SDK API changes โš ๏ธ: - - - -Build ๐Ÿงฑ: - - - -Test: - - - -Other changes: - - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd32991051..4b61777d3f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,9 +51,21 @@ If an issue does not exist yet, it may be relevant to open a new issue and let u This project is full Kotlin. Please do not write Java classes. -### CHANGES.md +### Changelog -Please add a line to the top of the file `CHANGES.md` describing your change. +Please create at least one file under ./newsfragment containing details about your change. Towncrier will be used when preparing the release. + +Towncrier says to use the PR number for the filename, but the issue number is also fine. + +Supported filename extensions are: + +- ``.feature``: Signifying a new feature in Element Android or in the Matrix SDK. +- ``.bugfix``: Signifying a bug fix. +- ``.doc``: Signifying a documentation improvement. +- ``.removal``: Signifying a deprecation or removal of public API. Can be used to notifying about API change in the Matrix SDK +- ``.misc``: A ticket has been closed, but it is not of interest to users. Note that in this case, the content of the file will not be output, but just the issue/PR number. + +See https://github.com/twisted/towncrier#news-fragments if you need more details. ### Code quality diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt index 6bb43d599c..6a4dd26392 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt @@ -47,14 +47,15 @@ import java.io.FileNotFoundException import java.io.IOException import javax.inject.Inject -internal class FileUploader @Inject constructor(@Authenticated - private val okHttpClient: OkHttpClient, - private val globalErrorReceiver: GlobalErrorReceiver, - private val homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService, - private val context: Context, - private val temporaryFileCreator: TemporaryFileCreator, - contentUrlResolver: ContentUrlResolver, - moshi: Moshi) { +internal class FileUploader @Inject constructor( + @Authenticated private val okHttpClient: OkHttpClient, + private val globalErrorReceiver: GlobalErrorReceiver, + private val homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService, + private val context: Context, + private val temporaryFileCreator: TemporaryFileCreator, + contentUrlResolver: ContentUrlResolver, + moshi: Moshi +) { private val uploadUrl = contentUrlResolver.uploadUrl private val responseAdapter = moshi.adapter(ContentUploadResponse::class.java) @@ -120,11 +121,17 @@ internal class FileUploader @Inject constructor(@Authenticated } } - private suspend fun upload(uploadBody: RequestBody, filename: String?, progressListener: ProgressRequestBody.Listener?): ContentUploadResponse { + private suspend fun upload(uploadBody: RequestBody, + filename: String?, + progressListener: ProgressRequestBody.Listener?): ContentUploadResponse { val urlBuilder = uploadUrl.toHttpUrlOrNull()?.newBuilder() ?: throw RuntimeException() val httpUrl = urlBuilder - .addQueryParameter("filename", filename) + .apply { + if (filename != null) { + addQueryParameter("filename", filename) + } + } .build() val requestBody = if (progressListener != null) ProgressRequestBody(uploadBody, progressListener) else uploadBody diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index d5c3deeec6..06cbf1ba90 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -229,7 +229,6 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter val encryptedFile: File? val contentUploadResponse = if (params.isEncrypted) { Timber.v("## Encrypt file") - encryptedFile = temporaryFileCreator.create() .also { filesToDelete.add(it) } @@ -239,16 +238,22 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong()) } } - Timber.v("## Uploading file") - - fileUploader - .uploadFile(encryptedFile, attachment.name, MimeTypes.OctetStream, progressListener) + fileUploader.uploadFile( + file = encryptedFile, + filename = null, + mimeType = MimeTypes.OctetStream, + progressListener = progressListener + ) } else { - Timber.v("## Clear file") + Timber.v("## Uploading clear file") encryptedFile = null - fileUploader - .uploadFile(fileToUpload, attachment.name, attachment.getSafeMimeType(), progressListener) + fileUploader.uploadFile( + file = fileToUpload, + filename = attachment.name, + mimeType = attachment.getSafeMimeType(), + progressListener = progressListener + ) } Timber.v("## Update cache storage for ${contentUploadResponse.contentUri}") @@ -312,7 +317,7 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter val encryptionResult = MXEncryptedAttachments.encryptAttachment(thumbnailData.bytes.inputStream(), thumbnailData.mimeType) val contentUploadResponse = fileUploader.uploadByteArray( byteArray = encryptionResult.encryptedByteArray, - filename = "thumb_${params.attachment.name}", + filename = null, mimeType = MimeTypes.OctetStream, progressListener = thumbnailProgressListener ) diff --git a/newsfragment/3293.misc b/newsfragment/3293.misc new file mode 100644 index 0000000000..66de1440e7 --- /dev/null +++ b/newsfragment/3293.misc @@ -0,0 +1 @@ +Setup towncrier tool \ No newline at end of file diff --git a/tools/towncrier/template.md b/tools/towncrier/template.md new file mode 100644 index 0000000000..fd160682b5 --- /dev/null +++ b/tools/towncrier/template.md @@ -0,0 +1,47 @@ +{% if top_line %} +{{ top_line }} +{{ top_underline * ((top_line)|length)}} +{% elif versiondata.name %} +{{ versiondata.name }} {{ versiondata.version }} ({{ versiondata.date }}) +{{ top_underline * ((versiondata.name + versiondata.version + versiondata.date)|length + 4)}} +{% else %} +{{ versiondata.version }} ({{ versiondata.date }}) +{{ top_underline * ((versiondata.version + versiondata.date)|length + 3)}} +{% endif %} +{% for section, _ in sections.items() %} +{% set underline = underlines[0] %}{% if section %}{{section}} +{{ underline * section|length }}{% set underline = underlines[1] %} +{% endif %} + +{% if sections[section] %} +{% for category, val in definitions.items() if category in sections[section]%} +{% if definitions[category]['name'] == "Features" %} +Features โœจ: +{% elif definitions[category]['name'] == "Bugfixes" %} +Bugfixes ๐Ÿ›: +{% elif definitions[category]['name'] == "Deprecations and Removals" %} +SDK API changes โš ๏ธ: +{% elif definitions[category]['name'] == "Improved Documentation" %} +Improved Documentation ๐Ÿ“š: +{% elif definitions[category]['name'] == "Misc" %} +Other changes: +{% else %} +{{ definitions[category]['name'] }} +{% endif %} +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category].items() %} + - {{ text }} ({{ values|join(', ') }}) +{% endfor %} +{% else %} + - {{ sections[section][category]['']|join(', ') }} +{% endif %} +{% if sections[section][category]|length == 0 %} +No significant changes. +{% else %} +{% endif %} + +{% endfor %} +{% else %} +No significant changes. +{% endif %} +{% endfor %} diff --git a/tools/travis/check_pr.sh b/tools/travis/check_pr.sh index 4c0d9611f0..b797244e55 100755 --- a/tools/travis/check_pr.sh +++ b/tools/travis/check_pr.sh @@ -23,7 +23,7 @@ branch=${TRAVIS_BRANCH} # If not on develop, exit, else we cannot get the list of modified files # It is ok to check only when on develop branch if [[ "${branch}" -eq 'develop' ]]; then - echo "Check that the file 'CHANGES.md' has been modified" + echo "Check that a file has been added to /newsfragment" else echo "Not on develop branch" exit 0 @@ -37,9 +37,9 @@ listOfModifiedFiles=`git diff --name-only HEAD ${branch}` # echo ${listOfModifiedFiles} -if [[ ${listOfModifiedFiles} = *"CHANGES.md"* ]]; then - echo "CHANGES.md has been modified!" +if [[ ${listOfModifiedFiles} = *"newsfragment"* ]]; then + echo "A file has been added to /newsfragment!" else - echo "โŒ Please add a line describing your change in CHANGES.md" + echo "โŒ Please add a file describing your changes in /newsfragment. See https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#changelog" exit 1 fi diff --git a/towncrier.toml b/towncrier.toml new file mode 100644 index 0000000000..09a927b77f --- /dev/null +++ b/towncrier.toml @@ -0,0 +1,7 @@ +[tool.towncrier] +directory = "newsfragment" +filename = "CHANGES.md" +name = "Changes in Element" +# Note: there is a bug, if I use title_format, the title is printed twice +# title_format = "Changes in Element {version} ({project_date})" +template="tools/towncrier/template.md" diff --git a/vector/src/main/java/im/vector/app/core/ui/list/GenericEmptyWithActionItem.kt b/vector/src/main/java/im/vector/app/core/ui/list/GenericEmptyWithActionItem.kt new file mode 100644 index 0000000000..f8eb968268 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/list/GenericEmptyWithActionItem.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package im.vector.app.core.ui.list + +import android.content.res.ColorStateList +import android.view.View +import android.widget.Button +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.ColorInt +import androidx.annotation.DrawableRes +import androidx.core.view.isVisible +import androidx.core.widget.ImageViewCompat +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.extensions.setTextOrHide + +/** + * A generic list item to display when there is no results, with an optional CTA + */ +@EpoxyModelClass(layout = R.layout.item_generic_empty_state) +abstract class GenericEmptyWithActionItem : VectorEpoxyModel() { + + class Action(var title: String) { + var perform: Runnable? = null + } + + @EpoxyAttribute + var title: CharSequence? = null + + @EpoxyAttribute + var description: CharSequence? = null + + @EpoxyAttribute + @DrawableRes + var iconRes: Int = -1 + + @EpoxyAttribute + @ColorInt + var iconTint: Int? = null + + @EpoxyAttribute + var buttonAction: Action? = null + + override fun bind(holder: Holder) { + super.bind(holder) + + holder.titleText.setTextOrHide(title) + holder.descriptionText.setTextOrHide(description) + + if (iconRes != -1) { + holder.imageView.setImageResource(iconRes) + holder.imageView.isVisible = true + if (iconTint != null) { + ImageViewCompat.setImageTintList(holder.imageView, ColorStateList.valueOf(iconTint!!)) + } else { + ImageViewCompat.setImageTintList(holder.imageView, null) + } + } else { + holder.imageView.isVisible = false + } + + holder.actionButton.setTextOrHide(buttonAction?.title) + holder.actionButton.setOnClickListener { + buttonAction?.perform?.run() + } + } + + class Holder : VectorEpoxyHolder() { + val root by bind(R.id.item_generic_root) + val titleText by bind(R.id.emptyItemTitleView) + val descriptionText by bind(R.id.emptyItemMessageView) + val imageView by bind(R.id.emptyItemImageView) + val actionButton by bind