From 910cb4ada023ac5ae9d5cc3fc9a6af562715a670 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 1 Nov 2023 13:19:48 -0700 Subject: [PATCH 01/13] feat(timeline): swipe to refresh (close #547) Signed-off-by: Varun Patil --- CHANGELOG.md | 1 + src/components/ScrollerManager.vue | 7 ++- src/components/SwipeRefresh.vue | 97 ++++++++++++++++++++++++++++++ src/components/Timeline.vue | 20 ++++-- 4 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 src/components/SwipeRefresh.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index bac68c8b..2ff1217e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - **Feature**: RAW files are now hidden (stacked) when another file with the same basename exists ([#537](https://github.com/pulsejet/memories/issues/537), [#152](https://github.com/pulsejet/memories/issues/152), [#419](https://github.com/pulsejet/memories/issues/419)) - **Feature**: Icon animation when playing live photos ([#898](https://github.com/pulsejet/memories/issues/898)) +- **Feature**: Swipe to refresh on timeline ([#547](https://github.com/pulsejet/memories/issues/547)) - **Bugfix**: Allow switching video to direct on Safari ([#650](https://github.com/pulsejet/memories/issues/650)) - Many other [bug fixes](https://github.com/pulsejet/memories/milestone/18?closed=1) - Android app is now open source ([see](https://github.com/pulsejet/memories/tree/master/android)) diff --git a/src/components/ScrollerManager.vue b/src/components/ScrollerManager.vue index 4ccf4d04..540cadba 100644 --- a/src/components/ScrollerManager.vue +++ b/src/components/ScrollerManager.vue @@ -95,6 +95,7 @@ export default defineComponent({ emits: { interactend: () => true, + scroll: (event: { current: number; previous: number }) => true, }, data: () => ({ @@ -223,11 +224,13 @@ export default defineComponent({ const scroll = this.recycler?.$el?.scrollTop || 0; // Emit scroll event - utils.bus.emit('memories.recycler.scroll', { + const event = { current: scroll, previous: this.lastKnownRecyclerScroll, dynTopMatterVisible: scroll < this.dynTopMatterHeight, - }); + }; + utils.bus.emit('memories.recycler.scroll', event); + this.$emit('scroll', event); this.lastKnownRecyclerScroll = scroll; // Get cursor px position diff --git a/src/components/SwipeRefresh.vue b/src/components/SwipeRefresh.vue new file mode 100644 index 00000000..9221c1a6 --- /dev/null +++ b/src/components/SwipeRefresh.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index 5623a6bb..a9788b19 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -1,5 +1,5 @@ + + diff --git a/src/components/viewer/Viewer.vue b/src/components/viewer/Viewer.vue index 8283fc56..8ad40922 100644 --- a/src/components/viewer/Viewer.vue +++ b/src/components/viewer/Viewer.vue @@ -155,6 +155,17 @@ + + {{ t('memories', 'Rotate / Flip') }} + + Date: Wed, 1 Nov 2023 18:50:00 -0700 Subject: [PATCH 07/13] refactor: remove log statement Signed-off-by: Varun Patil --- src/services/utils/fragment.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/utils/fragment.ts b/src/services/utils/fragment.ts index abb9e4e3..8e4cce1d 100644 --- a/src/services/utils/fragment.ts +++ b/src/services/utils/fragment.ts @@ -117,7 +117,6 @@ export const fragment = { // If the fragment is already in the list, we can't touch it. if (list.find((f) => f.type === frag.type)) { - console.debug('Fragment already in route', frag.type); return; } From 8eee97c619147c8f44d171e14219a64415b981a2 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 1 Nov 2023 18:50:13 -0700 Subject: [PATCH 08/13] viewer: fix edit meta call Signed-off-by: Varun Patil --- src/components/viewer/Viewer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/viewer/Viewer.vue b/src/components/viewer/Viewer.vue index 8ad40922..7c4fd79b 100644 --- a/src/components/viewer/Viewer.vue +++ b/src/components/viewer/Viewer.vue @@ -147,7 +147,7 @@ {{ t('memories', 'Edit metadata') }} From 60d390517d4bf4cf3ed43d7e2a8c49323c2b76d0 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 1 Nov 2023 19:22:36 -0700 Subject: [PATCH 09/13] edit-meta: minor CSS fix Signed-off-by: Varun Patil --- src/components/modal/EditMetadataModal.vue | 2 +- src/components/modal/EditOrientation.vue | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/modal/EditMetadataModal.vue b/src/components/modal/EditMetadataModal.vue index 3f7e07bf..5dabc06d 100644 --- a/src/components/modal/EditMetadataModal.vue +++ b/src/components/modal/EditMetadataModal.vue @@ -42,7 +42,7 @@
- {{ t('memories', 'Rotation') }} + {{ t('memories', 'Orientation (EXIF)') }}
diff --git a/src/components/modal/EditOrientation.vue b/src/components/modal/EditOrientation.vue index b44626f7..9030aaaa 100644 --- a/src/components/modal/EditOrientation.vue +++ b/src/components/modal/EditOrientation.vue @@ -215,7 +215,8 @@ export default defineComponent({ grid-gap: 5px; grid-template-columns: repeat(auto-fit, 80px); justify-content: center; - margin: 4px 0; + margin: 6px 0; + margin-top: 10px; .sample { border-radius: 10px; From 501ba618e46c40012df251cfb6a09603d832ce9d Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 2 Nov 2023 02:23:39 +0000 Subject: [PATCH 10/13] Fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- l10n/zh_CN.js | 16 ++++++++++++++++ l10n/zh_CN.json | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/l10n/zh_CN.js b/l10n/zh_CN.js index e0e1d80c..c0ddcf78 100644 --- a/l10n/zh_CN.js +++ b/l10n/zh_CN.js @@ -61,12 +61,20 @@ OC.L10N.register( "Timeline Path" : "时间线路径", "Square grid mode" : "方形网格模式", "Show past photos on top of timeline" : "在时间线顶部显示过去的照片", + "Stack RAW files with same name" : "堆叠同名 RAW 文件", + "Photo Viewer" : "照片浏览器", "Autoplay Live Photos" : "自动播放实时照片", + "Show full file path in sidebar" : "在侧边栏显示完整的文件路径", + "High resolution image loading behavior" : "高清图像加载偏好", + "Load high resolution image on zoom" : "在缩放时加载高清图像", + "Always load high resolution image (not recommended)" : "总是加载高清图像(不推荐)", + "Never load high resolution image" : "不加载高清图像", "Account" : "账户", "Logged in as {user}" : "以 {user} 登录", "Sign out" : "登出", "Device Folders" : "设备文件夹", "Local folders to include in the timeline view" : "要包含在时间线视图中的本地文件夹", + "Run initial device setup" : "运行设备初始化设定", "Folders Path" : "文件夹路径", "Show hidden folders" : "显示隐藏文件夹", "Sort folders oldest-first" : "将文件夹从最旧开始排序", @@ -81,6 +89,9 @@ OC.L10N.register( "Failed to update setting" : "更新设置失败", "Albums support is enabled through the Photos app." : "相册支持通过“照片”应用启用。", "Albums are disabled because the Photos app is not available." : "相册已禁用,因为“照片”应用不可用。", + "Recognize is installed and enabled for face recognition." : "Recognize 人脸识别已安装并启用。", + "Recognize is installed but not enabled for face recognition." : "Recognize 人脸识别已安装但并未启用。", + "Recognize is not installed. Face recognition and object tagging may be unavailable." : "Recognize 未安装。人脸识别和物品标记可能无法使用。", "Face Recognition is installed and enabled" : "人脸识别已安装并启用", "Preview generator is installed and enabled. Additional configuration may still be required." : "预览生成器已安装并启用。可能还需要额外的配置。", "Preview generator is not installed and configured. This may make Memories very slow." : "预览生成器未安装和配置。这可能会使“记忆”非常缓慢。", @@ -93,6 +104,11 @@ OC.L10N.register( "If you are using Imaginary for preview generation, you can ignore this section." : "如果您正在使用Imaginary生成预览,则可以忽略此部分。", "To enable RAW support, install the Camera RAW Previews app." : "要启用RAW支持,请安装Camera RAW Previews应用。", "Documentation." : "文档", + "PHP-Imagick is available [{version}]." : "PHP-Imagick 可用 [{version}]。", + "PHP-Imagick is not available." : "PHP-Imagick 不可用。", + "Image editing will not work correctly." : "图像编辑无法正常使用。", + "Thumbnail generation may not work for some formats (HEIC, TIFF)." : "缩略图生成可能不适用于某些格式(HEIC,TIFF)。", + "Thumbnails for videos will be generated with this binary." : "视频缩略图将使用此二进制文件生成。", "The following MIME types are configured for preview generation." : "为生成预览配置了以下MIME类型。", "Max preview size (trade-off between quality and storage requirements)." : "最大预览大小(质量和存储需求之间的权衡)", "Max memory for preview generation (MB)" : "生成预览时的最大内存(MB)", diff --git a/l10n/zh_CN.json b/l10n/zh_CN.json index 116c6e61..2a5de3a3 100644 --- a/l10n/zh_CN.json +++ b/l10n/zh_CN.json @@ -59,12 +59,20 @@ "Timeline Path" : "时间线路径", "Square grid mode" : "方形网格模式", "Show past photos on top of timeline" : "在时间线顶部显示过去的照片", + "Stack RAW files with same name" : "堆叠同名 RAW 文件", + "Photo Viewer" : "照片浏览器", "Autoplay Live Photos" : "自动播放实时照片", + "Show full file path in sidebar" : "在侧边栏显示完整的文件路径", + "High resolution image loading behavior" : "高清图像加载偏好", + "Load high resolution image on zoom" : "在缩放时加载高清图像", + "Always load high resolution image (not recommended)" : "总是加载高清图像(不推荐)", + "Never load high resolution image" : "不加载高清图像", "Account" : "账户", "Logged in as {user}" : "以 {user} 登录", "Sign out" : "登出", "Device Folders" : "设备文件夹", "Local folders to include in the timeline view" : "要包含在时间线视图中的本地文件夹", + "Run initial device setup" : "运行设备初始化设定", "Folders Path" : "文件夹路径", "Show hidden folders" : "显示隐藏文件夹", "Sort folders oldest-first" : "将文件夹从最旧开始排序", @@ -79,6 +87,9 @@ "Failed to update setting" : "更新设置失败", "Albums support is enabled through the Photos app." : "相册支持通过“照片”应用启用。", "Albums are disabled because the Photos app is not available." : "相册已禁用,因为“照片”应用不可用。", + "Recognize is installed and enabled for face recognition." : "Recognize 人脸识别已安装并启用。", + "Recognize is installed but not enabled for face recognition." : "Recognize 人脸识别已安装但并未启用。", + "Recognize is not installed. Face recognition and object tagging may be unavailable." : "Recognize 未安装。人脸识别和物品标记可能无法使用。", "Face Recognition is installed and enabled" : "人脸识别已安装并启用", "Preview generator is installed and enabled. Additional configuration may still be required." : "预览生成器已安装并启用。可能还需要额外的配置。", "Preview generator is not installed and configured. This may make Memories very slow." : "预览生成器未安装和配置。这可能会使“记忆”非常缓慢。", @@ -91,6 +102,11 @@ "If you are using Imaginary for preview generation, you can ignore this section." : "如果您正在使用Imaginary生成预览,则可以忽略此部分。", "To enable RAW support, install the Camera RAW Previews app." : "要启用RAW支持,请安装Camera RAW Previews应用。", "Documentation." : "文档", + "PHP-Imagick is available [{version}]." : "PHP-Imagick 可用 [{version}]。", + "PHP-Imagick is not available." : "PHP-Imagick 不可用。", + "Image editing will not work correctly." : "图像编辑无法正常使用。", + "Thumbnail generation may not work for some formats (HEIC, TIFF)." : "缩略图生成可能不适用于某些格式(HEIC,TIFF)。", + "Thumbnails for videos will be generated with this binary." : "视频缩略图将使用此二进制文件生成。", "The following MIME types are configured for preview generation." : "为生成预览配置了以下MIME类型。", "Max preview size (trade-off between quality and storage requirements)." : "最大预览大小(质量和存储需求之间的权衡)", "Max memory for preview generation (MB)" : "生成预览时的最大内存(MB)", From dc4e2ed9f800d5d50b5896d672bcbc6903e38bfc Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 1 Nov 2023 19:29:16 -0700 Subject: [PATCH 11/13] refactor(viewer): actions to array Signed-off-by: Varun Patil --- src/components/viewer/Viewer.vue | 323 ++++++++++++++----------------- 1 file changed, 150 insertions(+), 173 deletions(-) diff --git a/src/components/viewer/Viewer.vue b/src/components/viewer/Viewer.vue index 7c4fd79b..c6043479 100644 --- a/src/components/viewer/Viewer.vue +++ b/src/components/viewer/Viewer.vue @@ -22,159 +22,15 @@
- {{ t('memories', 'Share') }} - - - - {{ t('memories', 'Delete') }} - - - - {{ t('memories', 'Remove from album') }} - - - - {{ t('memories', 'Play Live Photo') }} + {{ action.name }} - - - {{ t('memories', 'Favorite') }} - - - - {{ t('memories', 'Info') }} - - - - {{ t('memories', 'Edit') }} - - - - {{ t('memories', 'Download') }} - - - - {{ t('memories', 'Download Video') }} - - - - {{ t('memories', 'Download {ext}', { ext: raw.extension }) }} - - - - {{ t('memories', 'View in folder') }} - - - - {{ t('memories', 'Slideshow') }} - - - - {{ t('memories', 'Edit metadata') }} - - - - {{ t('memories', 'Rotate / Flip') }} - - - - {{ t('memories', 'Add to album') }} - @@ -234,6 +90,21 @@ import AlbumRemoveIcon from 'vue-material-design-icons/BookRemove.vue'; import AlbumIcon from 'vue-material-design-icons/ImageAlbum.vue'; import RotateLeftIcon from 'vue-material-design-icons/RotateLeft.vue'; +type IViewerAction = { + /** Identifier (optional) */ + id: string; + /** Display text */ + name: string; + /** Icon component */ + icon: any; + /** Props on icon component */ + iconArgs?: any; + /** Action to perform */ + callback: () => void; + /** Condition to check for including */ + if: boolean; +}; + const SLIDESHOW_MS = 5000; const SIDEBAR_DEBOUNCE_MS = 350; const BODY_HAS_VIEWER = 'has-viewer'; @@ -246,20 +117,6 @@ export default defineComponent({ NcActions, NcActionButton, ImageEditor, - LivePhotoIcon, - ShareIcon, - DeleteIcon, - StarIcon, - StarOutlineIcon, - DownloadIcon, - InfoIcon, - OpenInNewIcon, - TuneIcon, - SlideshowIcon, - EditFileIcon, - AlbumRemoveIcon, - AlbumIcon, - RotateLeftIcon, }, mixins: [UserConfig], @@ -371,6 +228,126 @@ export default defineComponent({ return this.list[idx]; }, + /** Get all actions to show */ + actions(): IViewerAction[] { + return [ + { + id: 'share', + name: this.t('memories', 'Share'), + icon: ShareIcon, + callback: this.shareCurrent, + if: this.canShare, + }, + { + id: 'delete', + name: this.t('memories', 'Delete'), + icon: DeleteIcon, + callback: this.deleteCurrent, + if: !this.routeIsAlbums && this.canDelete, + }, + { + id: 'remove-from-album', + name: this.t('memories', 'Remove from album'), + icon: AlbumRemoveIcon, + callback: this.deleteCurrent, + if: this.routeIsAlbums, + }, + { + id: 'play-live-photo', + name: this.t('memories', 'Play Live Photo'), + icon: LivePhotoIcon, + iconArgs: { + playing: this.liveState.playing, + spin: this.liveState.waiting, + }, + callback: this.playLivePhoto, + if: this.isLivePhoto, + }, + { + id: 'favorite', + name: this.t('memories', 'Favorite'), + icon: this.isFavorite ? StarIcon : StarOutlineIcon, + callback: this.favoriteCurrent, + if: !this.routeIsPublic && !this.isLocal, + }, + { + id: 'info', + name: this.t('memories', 'Info'), + icon: InfoIcon, + callback: this.toggleSidebar, + if: true, + }, + { + id: 'edit', + name: this.t('memories', 'Edit'), + icon: TuneIcon, + callback: this.openEditor, + if: this.canEdit && !this.isVideo, + }, + { + id: 'download', + name: this.t('memories', 'Download'), + icon: DownloadIcon, + callback: this.downloadCurrent, + if: !this.initstate.noDownload && !this.isLocal, + }, + { + id: 'download-video', + name: this.t('memories', 'Download Video'), + icon: DownloadIcon, + callback: this.downloadCurrentLiveVideo, + if: !this.initstate.noDownload && !!this.currentPhoto?.liveid, + }, + ...this.stackedRaw.map((raw) => ({ + id: `download-raw-${raw.fileid}`, + name: this.t('memories', 'Download {ext}', { ext: raw.extension }), + icon: DownloadIcon, + callback: () => this.downloadByFileId(raw.fileid), + if: true, + })), + { + id: 'view-in-folder', + name: this.t('memories', 'View in folder'), + icon: OpenInNewIcon, + callback: this.viewInFolder, + if: !this.routeIsPublic && !this.routeIsAlbums && !this.isLocal, + }, + { + id: 'slideshow', + name: this.t('memories', 'Slideshow'), + icon: SlideshowIcon, + callback: this.startSlideshow, + if: this.globalCount > 1, + }, + { + id: 'edit-metadata', + name: this.t('memories', 'Edit metadata'), + icon: EditFileIcon, + callback: () => this.editMetadata(), + if: this.canEdit, + }, + { + id: 'rotate-flip', + name: this.t('memories', 'Rotate / Flip'), + icon: RotateLeftIcon, + callback: () => this.editMetadata([5]), + if: this.canEdit && !this.isVideo, + }, + { + id: 'add-to-album', + name: this.t('memories', 'Add to album'), + icon: AlbumIcon, + callback: this.updateAlbums, + if: + this.config.albums_enabled && + !this.isLocal && + !this.routeIsPublic && + this.canShare && + !!this.currentPhoto?.imageInfo?.filename, + }, + ].filter((action) => action.if); + }, + /** Is the current slide a video */ isVideo(): boolean { return Boolean((this.currentPhoto?.flag ?? 0) & this.c.FLAG_IS_VIDEO); @@ -386,6 +363,13 @@ export default defineComponent({ return utils.isLocalPhoto(this.currentPhoto!); }, + /** Is the current photo a favorite */ + isFavorite() { + const p = this.currentPhoto; + if (!p) return false; + return Boolean(p.flag & this.c.FLAG_IS_FAVORITE); + }, + /** Show bottom bar info such as date taken */ showBottomBar(): boolean { return !this.isVideo && this.fullyOpened && Boolean(this.currentPhoto?.imageInfo); @@ -1062,17 +1046,10 @@ export default defineComponent({ this.psLivePhoto?.play(this.photoswipe!.currSlide!.content as PsContent); }, - /** Is the current photo a favorite */ - isFavorite() { - const p = this.currentPhoto; - if (!p) return false; - return Boolean(p.flag & this.c.FLAG_IS_FAVORITE); - }, - /** Favorite the current photo */ async favoriteCurrent() { const photo = this.currentPhoto!; - const val = !this.isFavorite(); + const val = !this.isFavorite; try { this.updateLoading(1); for await (const p of dav.favoritePhotos([photo], val)) { From a7e7f80745429c0e8cbc77273569ac48b7b1e39c Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 1 Nov 2023 22:12:39 -0700 Subject: [PATCH 12/13] edit-orientation: add some warnings Signed-off-by: Varun Patil --- src/components/modal/EditMetadataModal.vue | 24 ++++++++++++++++------ src/components/modal/EditOrientation.vue | 8 +++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/components/modal/EditMetadataModal.vue b/src/components/modal/EditMetadataModal.vue index 5dabc06d..4f93484c 100644 --- a/src/components/modal/EditMetadataModal.vue +++ b/src/components/modal/EditMetadataModal.vue @@ -57,8 +57,6 @@