Immediatly show UI transfer for clipboard paste

Create an Upload model item before compressing the image and writing it
to disk
This commit is contained in:
miruka 2020-07-16 16:00:11 -04:00
parent bceafd7bbd
commit 5cc1c55d95
3 changed files with 37 additions and 20 deletions

View File

@ -18,8 +18,8 @@ from functools import partial
from pathlib import Path from pathlib import Path
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from typing import ( from typing import (
TYPE_CHECKING, Any, ClassVar, Dict, List, NamedTuple, Optional, Set, Tuple, TYPE_CHECKING, Any, Callable, ClassVar, Coroutine, Dict, List, NamedTuple,
Type, Union, Optional, Set, Tuple, Type, Union,
) )
from urllib.parse import urlparse from urllib.parse import urlparse
from uuid import UUID, uuid4 from uuid import UUID, uuid4
@ -56,7 +56,10 @@ if sys.version_info >= (3, 7):
else: else:
current_task = asyncio.Task.current_task current_task = asyncio.Task.current_task
CryptDict = Dict[str, Any] CryptDict = Dict[str, Any]
PathCallable = Union[
str, Path, Callable[[], Coroutine[None, None, Union[str, Path]]],
]
REPLY_FALLBACK = ( REPLY_FALLBACK = (
"<mx-reply>" "<mx-reply>"
@ -565,13 +568,20 @@ class MatrixClient(nio.AsyncClient):
prefix = datetime.now().strftime("%Y%m%d-%H%M%S.") prefix = datetime.now().strftime("%Y%m%d-%H%M%S.")
with NamedTemporaryFile(prefix=prefix, suffix=".png") as temp: with NamedTemporaryFile(prefix=prefix, suffix=".png") as temp:
async with aiofiles.open(temp.name, "wb") as file:
await file.write(image)
await self.send_file(room_id, temp.name) async def get_path() -> Path:
with io.BytesIO(image) as inp, io.BytesIO() as buffer:
PILImage.open(inp).save(buffer, "PNG", optimize=True)
async with aiofiles.open(temp.name, "wb") as file:
await file.write(buffer.getvalue())
return Path(temp.name)
await self.send_file(room_id, get_path)
async def send_file(self, room_id: str, path: Union[Path, str]) -> None: async def send_file(self, room_id: str, path: PathCallable) -> None:
"""Send a `m.file`, `m.image`, `m.audio` or `m.video` message.""" """Send a `m.file`, `m.image`, `m.audio` or `m.video` message."""
item_uuid = uuid4() item_uuid = uuid4()
@ -579,22 +589,26 @@ class MatrixClient(nio.AsyncClient):
try: try:
await self._send_file(item_uuid, room_id, path) await self._send_file(item_uuid, room_id, path)
except (nio.TransferCancelledError, asyncio.CancelledError): except (nio.TransferCancelledError, asyncio.CancelledError):
log.info("Deleting item for cancelled upload %s", item_uuid) self.upload_monitors.pop(item_uuid, None)
del self.upload_monitors[item_uuid] self.upload_tasks.pop(item_uuid, None)
del self.upload_tasks[item_uuid] self.models[room_id, "uploads"].pop(str(item_uuid), None)
del self.models[room_id, "uploads"][str(item_uuid)]
async def _send_file( async def _send_file(
self, item_uuid: UUID, room_id: str, path: Union[Path, str], self, item_uuid: UUID, room_id: str, path: PathCallable,
) -> None: ) -> None:
"""Upload and monitor a file + thumbnail and send the built event.""" """Upload and monitor a file + thumbnail and send the built event."""
# TODO: this function is way too complex, and most of it should be # TODO: this function is way too complex, and most of it should be
# refactored into nio. # refactored into nio.
self.upload_tasks[item_uuid] = current_task() # type: ignore
upload_item = Upload(item_uuid)
self.models[room_id, "uploads"][str(item_uuid)] = upload_item
transaction_id = uuid4() transaction_id = uuid4()
path = Path(path) path = Path(await path() if callable(path) else path)
encrypt = room_id in self.encrypted_rooms encrypt = room_id in self.encrypted_rooms
try: try:
@ -603,13 +617,12 @@ class MatrixClient(nio.AsyncClient):
# This error will be caught again by the try block later below # This error will be caught again by the try block later below
size = 0 size = 0
monitor = nio.TransferMonitor(size) upload_item.set_fields(
upload_item = Upload(item_uuid, path, total_size=size) status=UploadStatus.Uploading, filepath=path, total_size=size,
)
self.models[room_id, "uploads"][str(item_uuid)] = upload_item
monitor = nio.TransferMonitor(size)
self.upload_monitors[item_uuid] = monitor self.upload_monitors[item_uuid] = monitor
self.upload_tasks[item_uuid] = current_task() # type: ignore
def on_transferred(transferred: int) -> None: def on_transferred(transferred: int) -> None:
upload_item.uploaded = transferred upload_item.uploaded = transferred

View File

@ -311,6 +311,7 @@ class Member(ModelItem):
class UploadStatus(AutoStrEnum): class UploadStatus(AutoStrEnum):
"""Enum describing the status of an upload operation.""" """Enum describing the status of an upload operation."""
Preparing = auto()
Uploading = auto() Uploading = auto()
Caching = auto() Caching = auto()
Error = auto() Error = auto()
@ -321,7 +322,7 @@ class Upload(ModelItem):
"""Represent a running or failed file upload operation.""" """Represent a running or failed file upload operation."""
id: UUID = field() id: UUID = field()
filepath: Path = field() filepath: Path = Path("-")
total_size: int = 0 total_size: int = 0
uploaded: int = 0 uploaded: int = 0
@ -329,7 +330,7 @@ class Upload(ModelItem):
time_left: timedelta = timedelta(0) time_left: timedelta = timedelta(0)
paused: bool = False paused: bool = False
status: UploadStatus = UploadStatus.Uploading status: UploadStatus = UploadStatus.Preparing
error: OptionalExceptionType = type(None) error: OptionalExceptionType = type(None)
error_args: Tuple[Any, ...] = () error_args: Tuple[Any, ...] = ()

View File

@ -65,6 +65,9 @@ HColumnLayout {
cancelPending ? cancelPending ?
qsTr("Cancelling...") : qsTr("Cancelling...") :
status === "Preparing" ?
qsTr("Preparing file...") :
status === "Uploading" ? status === "Uploading" ?
fileName : fileName :