├── .cargo └── config.toml ├── .github └── workflows │ ├── build.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── INTERNALS.md ├── LICENSE.md ├── Makefile ├── README.md ├── bindings ├── prose-sdk-ffi │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── account_bookmarks_client.rs │ │ ├── client.rs │ │ ├── lib.rs │ │ ├── logger.rs │ │ ├── prose_sdk_ffi.udl │ │ ├── types │ │ │ ├── account_bookmark.rs │ │ │ ├── client_event.rs │ │ │ ├── contact.rs │ │ │ ├── jid.rs │ │ │ ├── message.rs │ │ │ └── mod.rs │ │ └── uniffi_api.rs │ └── uniffi.toml └── prose-sdk-js │ ├── .gitignore │ ├── Cargo.toml │ ├── LICENSE.md │ ├── README.md │ ├── package.json │ └── src │ ├── client.rs │ ├── connector │ ├── mod.rs │ └── strophe_js.rs │ ├── delegate.rs │ ├── encryption │ ├── encryption_service.rs │ ├── js_compat.rs │ ├── mod.rs │ └── signal_repo.rs │ ├── error.rs │ ├── error_hook.rs │ ├── lib.rs │ ├── log │ ├── js_log_writer.rs │ ├── js_logger.rs │ └── mod.rs │ └── types │ ├── account_info.rs │ ├── attachment.rs │ ├── channel.rs │ ├── connection_error.rs │ ├── contact.rs │ ├── jid.rs │ ├── js_array.rs │ ├── mention.rs │ ├── message.rs │ ├── message_result_set.rs │ ├── mod.rs │ ├── presence_sub_request.rs │ ├── room.rs │ ├── send_message_request.rs │ ├── sidebar_item.rs │ ├── upload_slot.rs │ ├── user_info.rs │ ├── user_metadata.rs │ ├── user_profile.rs │ └── workspace_info.rs ├── cliff-release-notes.toml ├── cliff.toml ├── crates ├── prose-core-client │ ├── Cargo.toml │ ├── src │ │ ├── app │ │ │ ├── deps │ │ │ │ ├── app_context.rs │ │ │ │ ├── app_dependencies.rs │ │ │ │ └── mod.rs │ │ │ ├── dtos │ │ │ │ ├── account_info.rs │ │ │ │ ├── contact.rs │ │ │ │ ├── message.rs │ │ │ │ ├── message_result_set.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── presence_sub_request.rs │ │ │ │ ├── room_envelope.rs │ │ │ │ ├── send_message_request.rs │ │ │ │ ├── sidebar_item.rs │ │ │ │ ├── upload_slot.rs │ │ │ │ ├── user_profile.rs │ │ │ │ └── workspace_info.rs │ │ │ ├── event_handlers │ │ │ │ ├── block_list_event_handler.rs │ │ │ │ ├── bookmarks_event_handler.rs │ │ │ │ ├── connection_event_handler.rs │ │ │ │ ├── contact_list_event_handler.rs │ │ │ │ ├── event_handler_queue.rs │ │ │ │ ├── messages_event_handler.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── requests_event_handler.rs │ │ │ │ ├── rooms_event_handler.rs │ │ │ │ ├── server_event.rs │ │ │ │ ├── synced_room_settings_event_handler.rs │ │ │ │ ├── user_devices_event_handler.rs │ │ │ │ ├── user_info_event_handler.rs │ │ │ │ └── workspace_info_event_handler.rs │ │ │ ├── mod.rs │ │ │ └── services │ │ │ │ ├── account_service.rs │ │ │ │ ├── block_list_service.rs │ │ │ │ ├── cache_service.rs │ │ │ │ ├── connection_service.rs │ │ │ │ ├── contact_list_service.rs │ │ │ │ ├── debug_service.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── preview_service.rs │ │ │ │ ├── room.rs │ │ │ │ ├── rooms_service.rs │ │ │ │ ├── sidebar_service.rs │ │ │ │ ├── upload_service.rs │ │ │ │ ├── user_data_service.rs │ │ │ │ └── workspace_service.rs │ │ ├── client.rs │ │ ├── client_builder.rs │ │ ├── client_event.rs │ │ ├── domain │ │ │ ├── account │ │ │ │ ├── mod.rs │ │ │ │ └── services │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── user_account_service.rs │ │ │ ├── connection │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── connection_properties.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── server_features.rs │ │ │ │ └── services │ │ │ │ │ ├── connection_service.rs │ │ │ │ │ └── mod.rs │ │ │ ├── contacts │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── contact.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── presence_sub_request.rs │ │ │ │ ├── repos │ │ │ │ │ ├── block_list_repository.rs │ │ │ │ │ ├── contact_list_repository.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── presence_sub_requests_repository.rs │ │ │ │ └── services │ │ │ │ │ ├── block_list_domain_service.rs │ │ │ │ │ ├── block_list_service.rs │ │ │ │ │ ├── contact_list_domain_service.rs │ │ │ │ │ ├── contact_list_service.rs │ │ │ │ │ ├── impls │ │ │ │ │ ├── block_list_domain_service.rs │ │ │ │ │ ├── contact_list_domain_service.rs │ │ │ │ │ └── mod.rs │ │ │ │ │ └── mod.rs │ │ │ ├── encryption │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── decryption_context.rs │ │ │ │ │ ├── device.rs │ │ │ │ │ ├── device_bundle.rs │ │ │ │ │ ├── device_id.rs │ │ │ │ │ ├── device_info.rs │ │ │ │ │ ├── keys.rs │ │ │ │ │ ├── local_device.rs │ │ │ │ │ ├── local_encryption_bundle.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── session.rs │ │ │ │ ├── repos │ │ │ │ │ ├── encryption_keys_repository.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── session_repository.rs │ │ │ │ │ └── user_device_repository.rs │ │ │ │ └── services │ │ │ │ │ ├── encryption_domain_service.rs │ │ │ │ │ ├── encryption_service.rs │ │ │ │ │ ├── impls │ │ │ │ │ ├── encryption_domain_service.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── signal_native │ │ │ │ │ │ ├── mod.rs │ │ │ │ │ │ ├── signal_compat.rs │ │ │ │ │ │ ├── signal_repo_wrapper.rs │ │ │ │ │ │ └── signal_service.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── user_device_id_provider.rs │ │ │ │ │ └── user_device_service.rs │ │ │ ├── general │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── capabilities.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── software_version.rs │ │ │ │ └── services │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── request_handling_service.rs │ │ │ ├── messaging │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── attachment.rs │ │ │ │ │ ├── encrypted_message.rs │ │ │ │ │ ├── error.rs │ │ │ │ │ ├── mention.rs │ │ │ │ │ ├── message.rs │ │ │ │ │ ├── message_id.rs │ │ │ │ │ ├── message_like.rs │ │ │ │ │ ├── message_parser.rs │ │ │ │ │ ├── message_ref.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── send_message_request.rs │ │ │ │ ├── repos │ │ │ │ │ ├── drafts_repository.rs │ │ │ │ │ ├── messages_repository.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── offline_messages_repository.rs │ │ │ │ └── services │ │ │ │ │ ├── impls │ │ │ │ │ ├── message_archive_domain_service.rs │ │ │ │ │ ├── message_migration_domain_service.rs │ │ │ │ │ └── mod.rs │ │ │ │ │ ├── message_archive_domain_service.rs │ │ │ │ │ ├── message_archive_service.rs │ │ │ │ │ ├── message_id_provider.rs │ │ │ │ │ ├── message_migration_domain_service.rs │ │ │ │ │ ├── messaging_service.rs │ │ │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── rooms │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── compose_state.rs │ │ │ │ │ ├── constants.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── participant_list.rs │ │ │ │ │ ├── public_room_info.rs │ │ │ │ │ ├── room.rs │ │ │ │ │ ├── room_affiliation.rs │ │ │ │ │ ├── room_error.rs │ │ │ │ │ ├── room_features.rs │ │ │ │ │ ├── room_session_info.rs │ │ │ │ │ └── room_spec.rs │ │ │ │ ├── repos │ │ │ │ │ ├── connected_rooms_repository.rs │ │ │ │ │ └── mod.rs │ │ │ │ └── services │ │ │ │ │ ├── impls │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── room_utils.rs │ │ │ │ │ └── rooms_domain_service.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── room_attributes_service.rs │ │ │ │ │ ├── room_factory.rs │ │ │ │ │ ├── room_management_service.rs │ │ │ │ │ ├── room_participation_service.rs │ │ │ │ │ └── rooms_domain_service.rs │ │ │ ├── settings │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── account_settings.rs │ │ │ │ │ ├── local_room_settings.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── synced_room_settings.rs │ │ │ │ ├── repos │ │ │ │ │ ├── account_settings_repository.rs │ │ │ │ │ ├── local_room_settings_repository.rs │ │ │ │ │ └── mod.rs │ │ │ │ └── services │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── synced_room_settings_service.rs │ │ │ ├── shared │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── account_id.rs │ │ │ │ │ ├── anon_occupant_id.rs │ │ │ │ │ ├── availability.rs │ │ │ │ │ ├── avatar.rs │ │ │ │ │ ├── avatar_id.rs │ │ │ │ │ ├── avatar_metadata.rs │ │ │ │ │ ├── bare_entity_id.rs │ │ │ │ │ ├── cache_policy.rs │ │ │ │ │ ├── capabilities_id.rs │ │ │ │ │ ├── connection_state.rs │ │ │ │ │ ├── entity_id.rs │ │ │ │ │ ├── mam_version.rs │ │ │ │ │ ├── message.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── muc_id.rs │ │ │ │ │ ├── occupant_id.rs │ │ │ │ │ ├── participant_id.rs │ │ │ │ │ ├── request_id.rs │ │ │ │ │ ├── room_id.rs │ │ │ │ │ ├── room_type.rs │ │ │ │ │ ├── sender_id.rs │ │ │ │ │ ├── server_id.rs │ │ │ │ │ ├── string_index.rs │ │ │ │ │ ├── user_endpoint_id.rs │ │ │ │ │ ├── user_id.rs │ │ │ │ │ ├── user_info.rs │ │ │ │ │ ├── user_or_resource_id.rs │ │ │ │ │ └── user_resource_id.rs │ │ │ │ └── utils │ │ │ │ │ ├── contact_name_builder.rs │ │ │ │ │ └── mod.rs │ │ │ ├── sidebar │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── bookmark.rs │ │ │ │ │ ├── bookmark_type.rs │ │ │ │ │ └── mod.rs │ │ │ │ └── services │ │ │ │ │ ├── bookmarks_service.rs │ │ │ │ │ ├── impls │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── sidebar_domain_service.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── sidebar_domain_service.rs │ │ │ ├── uploads │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── upload_slot.rs │ │ │ │ └── services │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── upload_service.rs │ │ │ ├── user_info │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ │ ├── jabber_client.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── platform_image.rs │ │ │ │ │ ├── presence.rs │ │ │ │ │ ├── user_info.rs │ │ │ │ │ ├── user_metadata.rs │ │ │ │ │ ├── user_profile.rs │ │ │ │ │ └── user_status.rs │ │ │ │ ├── repos │ │ │ │ │ ├── avatar_repository.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── user_info_repository.rs │ │ │ │ │ └── user_profile_repository.rs │ │ │ │ └── services │ │ │ │ │ ├── impls │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── user_info_domain_service.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── user_info_domain_service.rs │ │ │ │ │ └── user_info_service.rs │ │ │ └── workspace │ │ │ │ ├── mod.rs │ │ │ │ ├── models │ │ │ │ ├── mod.rs │ │ │ │ └── workspace_info.rs │ │ │ │ ├── repos │ │ │ │ ├── mod.rs │ │ │ │ └── workspace_info_repository.rs │ │ │ │ └── services │ │ │ │ ├── impls │ │ │ │ ├── mod.rs │ │ │ │ └── workspace_info_domain_service.rs │ │ │ │ ├── mod.rs │ │ │ │ └── workspace_info_domain_service.rs │ │ ├── infra │ │ │ ├── account │ │ │ │ ├── mod.rs │ │ │ │ └── user_account_service.rs │ │ │ ├── connection │ │ │ │ ├── connection_service.rs │ │ │ │ └── mod.rs │ │ │ ├── contacts │ │ │ │ ├── block_list_service.rs │ │ │ │ ├── caching_block_list_repository.rs │ │ │ │ ├── caching_contacts_repository.rs │ │ │ │ ├── contact_list_service.rs │ │ │ │ ├── mod.rs │ │ │ │ └── presence_sub_requests_repository.rs │ │ │ ├── encryption │ │ │ │ ├── caching_user_device_repository.rs │ │ │ │ ├── encryption_key_records.rs │ │ │ │ ├── encryption_keys_repository.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── session_repository.rs │ │ │ │ └── user_device_service.rs │ │ │ ├── events │ │ │ │ ├── coalescing_client_event_dispatcher.rs │ │ │ │ ├── immediate_client_event_dispatcher.rs │ │ │ │ └── mod.rs │ │ │ ├── general │ │ │ │ ├── mod.rs │ │ │ │ ├── nano_id_provider.rs │ │ │ │ ├── request_handling_service.rs │ │ │ │ └── rng_provider.rs │ │ │ ├── messaging │ │ │ │ ├── caching_message_repository.rs │ │ │ │ ├── drafts_repository.rs │ │ │ │ ├── message_archive_service.rs │ │ │ │ ├── message_record.rs │ │ │ │ ├── messaging_service.rs │ │ │ │ ├── mod.rs │ │ │ │ └── offline_messages_repository.rs │ │ │ ├── mod.rs │ │ │ ├── platform_dependencies.rs │ │ │ ├── rooms │ │ │ │ ├── in_memory_connected_rooms_repository.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── room_attributes_service.rs │ │ │ │ ├── room_management_service.rs │ │ │ │ └── room_participation_service.rs │ │ │ ├── settings │ │ │ │ ├── account_settings_repository.rs │ │ │ │ ├── local_room_settings_repository.rs │ │ │ │ ├── mod.rs │ │ │ │ └── synced_room_settings_service.rs │ │ │ ├── sidebar │ │ │ │ ├── bookmarks_service.rs │ │ │ │ └── mod.rs │ │ │ ├── uploads │ │ │ │ ├── mod.rs │ │ │ │ └── upload_service.rs │ │ │ ├── user_info │ │ │ │ ├── fs_avatar_repository.rs │ │ │ │ ├── in_memory_user_info_repository.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── presence_map.rs │ │ │ │ ├── store_avatar_repository.rs │ │ │ │ ├── user_info_service.rs │ │ │ │ └── user_profile_repository.rs │ │ │ ├── workspace │ │ │ │ ├── mod.rs │ │ │ │ └── workspace_info_repository.rs │ │ │ └── xmpp │ │ │ │ ├── event_parser │ │ │ │ ├── message.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── presence.rs │ │ │ │ └── pubsub │ │ │ │ │ ├── generic_pub_sub_parser.rs │ │ │ │ │ └── mod.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── type_conversions │ │ │ │ ├── attachment.rs │ │ │ │ ├── availability.rs │ │ │ │ ├── avatar_metadata.rs │ │ │ │ ├── bookmark.rs │ │ │ │ ├── caps.rs │ │ │ │ ├── compose_state.rs │ │ │ │ ├── contact.rs │ │ │ │ ├── device_bundle.rs │ │ │ │ ├── encrypted_payload.rs │ │ │ │ ├── mention.rs │ │ │ │ ├── message_ref.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── room_affiliation.rs │ │ │ │ ├── room_info.rs │ │ │ │ ├── room_session_participant.rs │ │ │ │ ├── room_spec.rs │ │ │ │ ├── stanza_error.rs │ │ │ │ ├── synced_room_settings.rs │ │ │ │ ├── thumbnail.rs │ │ │ │ ├── upload_slot.rs │ │ │ │ ├── user_activity.rs │ │ │ │ ├── user_device.rs │ │ │ │ ├── user_profile.rs │ │ │ │ └── workspace_info.rs │ │ │ │ ├── util │ │ │ │ ├── caps_ext.rs │ │ │ │ ├── file_ext.rs │ │ │ │ ├── jid_ext.rs │ │ │ │ ├── media_share_ext.rs │ │ │ │ ├── message_ext.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── presence_ext.rs │ │ │ │ └── room_occupancy_ext.rs │ │ │ │ └── xmpp_client.rs │ │ ├── lib.rs │ │ ├── test │ │ │ ├── bookmark.rs │ │ │ ├── constant_time_provider.rs │ │ │ ├── message_builder.rs │ │ │ ├── mock_app_dependencies.rs │ │ │ ├── mod.rs │ │ │ ├── room_internals.rs │ │ │ └── room_metadata.rs │ │ └── util │ │ │ ├── account_bookmarks_client.rs │ │ │ ├── coalesce_client_events.rs │ │ │ ├── form_config.rs │ │ │ ├── jid_workspace.rs │ │ │ ├── join_all.rs │ │ │ ├── mime_serde_shim.rs │ │ │ ├── mod.rs │ │ │ ├── path_ext.rs │ │ │ ├── proxy_transformer.rs │ │ │ └── string_ext.rs │ └── tests │ │ ├── account_service.rs │ │ ├── connection_service.rs │ │ ├── contacts_service.rs │ │ ├── event_parsing │ │ ├── connection_event_parser.rs │ │ ├── contact_list_event_parser.rs │ │ ├── main.rs │ │ ├── message_event_parser.rs │ │ ├── request_event_parser.rs │ │ ├── room_event_parser.rs │ │ ├── sidebar_bookmark_event_parser.rs │ │ ├── user_device_event_parser.rs │ │ └── user_event_parser.rs │ │ ├── message_parsing │ │ ├── main.rs │ │ └── message_parser.rs │ │ ├── messages_event_handler.rs │ │ ├── requests_event_handler.rs │ │ ├── room.rs │ │ ├── rooms_domain_service.rs │ │ ├── rooms_event_handler.rs │ │ ├── rooms_service.rs │ │ ├── sidebar_domain_service.rs │ │ └── user_info_domain_service.rs ├── prose-markup │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ └── styling_writer.rs │ └── tests │ │ ├── fixtures │ │ ├── complex.md │ │ ├── html_tags.md │ │ ├── links.md │ │ ├── nested_blockquotes.md │ │ ├── nested_lists.md │ │ └── nested_spans.md │ │ ├── snapshots │ │ ├── styling_writer__complex.snap │ │ ├── styling_writer__escapes_html_tags.snap │ │ ├── styling_writer__links.snap │ │ ├── styling_writer__nested_blockquotes.snap │ │ ├── styling_writer__nested_lists.snap │ │ └── styling_writer__nested_spans.snap │ │ └── styling_writer.rs ├── prose-proc-macros │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── prose-store │ ├── Cargo.toml │ └── src │ │ ├── driver │ │ ├── indexed_db.rs │ │ ├── mod.rs │ │ └── sqlite.rs │ │ ├── entity_macro.rs │ │ ├── lib.rs │ │ ├── prelude.rs │ │ ├── repository.rs │ │ └── store.rs ├── prose-utils │ ├── Cargo.toml │ └── src │ │ ├── id_string_macro.rs │ │ └── lib.rs ├── prose-wasm-utils │ ├── Cargo.toml │ └── src │ │ ├── future_ext.rs │ │ ├── lib.rs │ │ ├── receiver_stream.rs │ │ └── stream_ext.rs └── prose-xmpp │ ├── Cargo.toml │ ├── src │ ├── client │ │ ├── builder.rs │ │ ├── client.rs │ │ ├── mod.rs │ │ └── module_context.rs │ ├── connector │ │ ├── connector.rs │ │ ├── mod.rs │ │ ├── proxy_connector.rs │ │ └── xmpp_rs.rs │ ├── deps │ │ ├── id_provider.rs │ │ ├── mod.rs │ │ └── time_provider.rs │ ├── event.rs │ ├── lib.rs │ ├── mods │ │ ├── block_list.rs │ │ ├── bookmark.rs │ │ ├── bookmark2.rs │ │ ├── caps.rs │ │ ├── chat.rs │ │ ├── http_upload.rs │ │ ├── mam.rs │ │ ├── mod.rs │ │ ├── muc │ │ │ ├── join_room_future.rs │ │ │ ├── mod.rs │ │ │ └── send_muc_message_future.rs │ │ ├── omemo.rs │ │ ├── ping.rs │ │ ├── profile.rs │ │ ├── pubsub.rs │ │ ├── roster.rs │ │ └── status.rs │ ├── stanza │ │ ├── avatar.rs │ │ ├── conference_bookmark.rs │ │ ├── last_activity.rs │ │ ├── mam │ │ │ ├── mod.rs │ │ │ └── query.rs │ │ ├── media_sharing │ │ │ ├── file.rs │ │ │ ├── media_share.rs │ │ │ ├── mod.rs │ │ │ ├── oob.rs │ │ │ └── thumbnail.rs │ │ ├── message │ │ │ ├── builder.rs │ │ │ ├── carbons.rs │ │ │ ├── chat_marker.rs │ │ │ ├── content.rs │ │ │ ├── fallback.rs │ │ │ ├── fasten.rs │ │ │ ├── forwarding.rs │ │ │ ├── mam.rs │ │ │ ├── message.rs │ │ │ ├── mod.rs │ │ │ ├── muc_invite.rs │ │ │ ├── muc_user.rs │ │ │ ├── reactions.rs │ │ │ ├── reply.rs │ │ │ ├── retract.rs │ │ │ └── stanza_id.rs │ │ ├── mod.rs │ │ ├── muc │ │ │ ├── direct_invite.rs │ │ │ ├── mediated_invite.rs │ │ │ ├── mod.rs │ │ │ ├── muc_user.rs │ │ │ ├── ns.rs │ │ │ └── query.rs │ │ ├── ns.rs │ │ ├── omemo │ │ │ ├── device.rs │ │ │ ├── device_list.rs │ │ │ └── mod.rs │ │ ├── pubsub.rs │ │ ├── references │ │ │ ├── mod.rs │ │ │ └── reference.rs │ │ ├── user_activity.rs │ │ ├── vcard.rs │ │ └── vcard4.rs │ ├── test │ │ ├── connected_client.rs │ │ ├── connector.rs │ │ ├── constant_id_provider.rs │ │ ├── element_ext.rs │ │ ├── incrementing_id_provider.rs │ │ └── mod.rs │ └── util │ │ ├── element_ext.rs │ │ ├── item_id_ext.rs │ │ ├── mod.rs │ │ ├── module_future_state.rs │ │ ├── pub_sub_items_ext.rs │ │ ├── pub_sub_query.rs │ │ ├── publish_options_ext.rs │ │ ├── request_error.rs │ │ ├── request_future.rs │ │ └── xmpp_element.rs │ └── tests │ ├── bookmark_mod.rs │ ├── chat_mod.rs │ ├── muc_mod.rs │ └── snapshots │ ├── bookmark_mod__publishes_bookmark.snap │ └── bookmark_mod__publishes_legacy_bookmarks.snap ├── examples ├── .env.example ├── .gitignore ├── README.md ├── common │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── prose-core-client-cli │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ ├── type_display.rs │ │ └── type_selection.rs └── xmpp-client │ ├── Cargo.toml │ └── src │ └── main.rs ├── prose.doap ├── tests ├── prose-core-integration-tests │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── tests │ │ ├── account_settings_repository.rs │ │ ├── client │ │ ├── avatar.rs │ │ ├── catchup_unread.rs │ │ ├── contact_list.rs │ │ ├── helpers │ │ │ ├── connector.rs │ │ │ ├── delegate.rs │ │ │ ├── element_ext.rs │ │ │ ├── id_provider.rs │ │ │ ├── mod.rs │ │ │ ├── test_client.rs │ │ │ ├── test_client_login.rs │ │ │ ├── test_client_muc.rs │ │ │ ├── test_client_omemo.rs │ │ │ └── test_message_queue.rs │ │ ├── message_handling.rs │ │ ├── message_styling.rs │ │ ├── mod.rs │ │ ├── muc.rs │ │ ├── muc_omemo.rs │ │ ├── omemo.rs │ │ ├── reactions.rs │ │ ├── reconnect.rs │ │ ├── reply.rs │ │ ├── user_info.rs │ │ └── workspace.rs │ │ ├── contacts_repository.rs │ │ ├── drafts_repository.rs │ │ ├── local_room_settings_repository.rs │ │ ├── messages_repository.rs │ │ ├── mod.rs │ │ ├── user_info_repository.rs │ │ └── workspace_info_repository.rs └── prose-store-integration-tests │ ├── Cargo.toml │ └── src │ ├── lib.rs │ └── tests │ ├── mod.rs │ ├── repository.rs │ ├── snapshots │ ├── prose_store_integration_tests__tests__sqlite__query_uses_index.snap │ ├── prose_store_integration_tests__tests__sqlite__query_uses_multicolumn_index.snap │ └── prose_store_integration_tests__tests__sqlite__table_structure.snap │ └── sqlite.rs └── xtask ├── Cargo.toml └── src ├── ci.rs ├── main.rs ├── swift.rs └── wasm.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | 4 | [build] 5 | #target = "wasm32-unknown-unknown" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nova/ 2 | .idea/ 3 | .target-run/ 4 | 5 | target 6 | Cargo.lock 7 | 8 | .env 9 | .npmrc 10 | rustc-ice-*.txt -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "crates/*", 4 | "bindings/*", 5 | "examples/*", 6 | "tests/*", 7 | "xtask", 8 | ] 9 | default-members = ["crates/*"] 10 | resolver = "2" 11 | 12 | [workspace.package] 13 | rust-version = "1.70" 14 | 15 | [workspace.dependencies] 16 | anyhow = { version = "1.0", features = ["backtrace"] } 17 | async-trait = "0.1" 18 | base64 = "0.22" 19 | chrono = "0.4" 20 | futures = "0.3" 21 | insta = "1.3" 22 | itertools = "0.13.0" 23 | jid = { version = "0.11", default-features = false } 24 | js-sys = "0.3" 25 | mime = "0.3" 26 | minidom = "0.16" 27 | parking_lot = "0.12" 28 | pretty_assertions = "1.4" 29 | secrecy = "0.10" 30 | serde = "1.0" 31 | serde_json = "1.0" 32 | sha1 = "0.10" 33 | strum = "0.26" 34 | strum_macros = "0.26" 35 | tempfile = "3.5" 36 | thiserror = "1.0" 37 | tokio = "1.26" 38 | tokio-xmpp = "4.0" 39 | tracing = { version = "0.1" } 40 | tracing-log = "0.2.0" 41 | tracing-subscriber = "0.3" 42 | uniffi = "0.28" 43 | url = "2.3" 44 | uuid = { version = "1.1", features = ["v4", "fast-rng", "macro-diagnostics"] } 45 | wasm-bindgen = { version = "0.2" } 46 | wasm-bindgen-futures = "0.4" 47 | xmpp-parsers = { version = "0.21", features = ["disable-validation"] } 48 | xso = "0.1" 49 | 50 | [profile.release] 51 | lto = true 52 | -------------------------------------------------------------------------------- /INTERNALS.md: -------------------------------------------------------------------------------- 1 | # Internals — Prose Core Client 2 | 3 | ## The Big Picture 4 | 5 | A Prose client can be built with the following code: 6 | 7 | ```rust 8 | let client = ProseClientBuilder::new() 9 | .app(ProseClientOrigin::ProseAppMacOS) 10 | .build()? 11 | .bind()?; 12 | ``` 13 | 14 | Internally, this is a group of sub-clients, aka. accounts (a client has 1 or many accounts connected). In most cases, the Prose app will be bound to a single account anyway. 15 | 16 | The client manager binds on a dedicated thread, and spawns its data store threads and client thread. It connects to each XMPP account stored in the database, and exposes convenience methods to send prepared payloads, or receive payloads. It gives away a `ProseClient`, which is then used to access and manage accounts (where 1 account = 1 sub-client). 17 | 18 | So, stores and brokers get isolated per-account. The implementer application (eg. the macOS app) is given a list of clients on which it can bind and unbind its handlers at its whim. Thus, when switching to another Prose team in the UI, the user can simply use another client and rebind its event listeners on the ingress broker. 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | preflight: 2 | cargo fmt -- --check 3 | cargo build 4 | cargo build --package prose-sdk-js --target wasm32-unknown-unknown 5 | cargo build --package prose-sdk-ffi 6 | cargo build --package prose-core-client-cli --package xmpp-client 7 | cargo test --features test 8 | cargo test --package prose-core-integration-tests 9 | cargo test --package prose-store-integration-tests 10 | cargo xtask ci wasm 11 | cargo xtask ci wasm-store -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/.gitignore: -------------------------------------------------------------------------------- 1 | generated 2 | ProseSDK -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-sdk-ffi" 3 | version = "0.1.0" 4 | description = "Prose core XMPP client FFIs." 5 | license = "MPL-2.0" 6 | edition = "2021" 7 | homepage = "https://github.com/prose-im/prose-core-client" 8 | repository = "https://github.com/prose-im/prose-core-client.git" 9 | keywords = ["xmpp", "xmpp-client", "library"] 10 | categories = ["network-programming"] 11 | authors = ["Marc Bauer "] 12 | 13 | [lib] 14 | name = "prose_sdk_ffi" 15 | crate-type = ["staticlib", "lib"] 16 | 17 | [dependencies] 18 | anyhow = { workspace = true } 19 | chrono = { workspace = true } 20 | jid = { workspace = true } 21 | parking_lot = { workspace = true } 22 | prose-core-client = { path = "../../crates/prose-core-client" } 23 | prose-xmpp = { path = "../../crates/prose-xmpp" } 24 | thiserror = { workspace = true } 25 | tracing = { workspace = true } 26 | tracing-subscriber = { workspace = true } 27 | uniffi = { workspace = true, features = ["tokio"] } 28 | 29 | [build-dependencies] 30 | uniffi = { workspace = true, features = ["build"] } -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/build.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-ffi 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | fn main() { 7 | uniffi::generate_scaffolding("./src/prose_sdk_ffi.udl").expect("Failed to build UDL file"); 8 | } 9 | -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/src/lib.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-ffi 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use prose_core_client::FsAvatarRepositoryError; 7 | pub use uniffi_api::*; 8 | 9 | mod account_bookmarks_client; 10 | mod client; 11 | mod logger; 12 | mod types; 13 | mod uniffi_api; 14 | 15 | #[derive(thiserror::Error, Debug)] 16 | pub enum ClientError { 17 | #[error("client error: {msg}")] 18 | Generic { msg: String }, 19 | } 20 | 21 | impl From for ClientError { 22 | fn from(e: anyhow::Error) -> ClientError { 23 | ClientError::Generic { msg: e.to_string() } 24 | } 25 | } 26 | 27 | impl From for ClientError { 28 | fn from(e: FsAvatarRepositoryError) -> Self { 29 | ClientError::Generic { msg: e.to_string() } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/src/types/account_bookmark.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-ffi 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::types::JID; 7 | use prose_core_client::AccountBookmark as ProseAccountBookmark; 8 | 9 | pub struct AccountBookmark { 10 | pub jid: JID, 11 | pub is_selected: bool, 12 | } 13 | 14 | impl From for AccountBookmark { 15 | fn from(value: ProseAccountBookmark) -> Self { 16 | AccountBookmark { 17 | jid: value.jid.into(), 18 | is_selected: value.is_selected, 19 | } 20 | } 21 | } 22 | 23 | impl From for ProseAccountBookmark { 24 | fn from(value: AccountBookmark) -> Self { 25 | ProseAccountBookmark { 26 | jid: value.jid.into(), 27 | is_selected: value.is_selected, 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/src/types/contact.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-ffi 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::types::JID; 7 | use prose_core_client::dtos::{ 8 | Availability, Contact as CoreContact, Group as CoreGroup, UserStatus, 9 | }; 10 | 11 | #[derive(Debug, PartialEq, Clone)] 12 | pub enum Group { 13 | Team, 14 | Other, 15 | } 16 | 17 | #[derive(Debug, PartialEq, Clone)] 18 | pub struct Contact { 19 | pub jid: JID, 20 | pub name: String, 21 | pub availability: Availability, 22 | pub status: Option, 23 | pub group: Group, 24 | } 25 | 26 | impl From for Contact { 27 | fn from(value: CoreContact) -> Self { 28 | Contact { 29 | jid: value.id.into_inner().into(), 30 | name: value.name, 31 | availability: value.availability, 32 | status: value.status, 33 | group: value.group.into(), 34 | } 35 | } 36 | } 37 | 38 | impl From for Group { 39 | fn from(value: CoreGroup) -> Self { 40 | match value { 41 | CoreGroup::Team => Group::Team, 42 | CoreGroup::Other => Group::Other, 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-ffi 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use account_bookmark::AccountBookmark; 7 | pub use client_event::ClientEvent; 8 | pub use contact::{Contact, Group}; 9 | pub use jid::{parse_jid, JID}; 10 | pub use message::{DateTime, Message, Reaction}; 11 | 12 | mod account_bookmark; 13 | mod client_event; 14 | mod contact; 15 | mod jid; 16 | mod message; 17 | -------------------------------------------------------------------------------- /bindings/prose-sdk-ffi/uniffi.toml: -------------------------------------------------------------------------------- 1 | [bindings.swift] 2 | cdylib_name = "prose-sdk-ffi" 3 | module_name = "prose_sdk_ffi" 4 | 5 | [bindings.swift.custom_types.PathBuf] 6 | type_name = "URL" 7 | imports = [ "Foundation" ] 8 | into_custom = "URL(fileURLWithPath: {})" 9 | from_custom = "{}.path" 10 | 11 | [bindings.swift.custom_types.Url] 12 | type_name = "URL" 13 | imports = [ "Foundation" ] 14 | into_custom = "URL(string: {})!" 15 | from_custom = "{}.absoluteString" 16 | 17 | [bindings.swift.custom_types.DateTime] 18 | type_name = "Date" 19 | imports = [ "Foundation" ] 20 | into_custom = "Date(timeIntervalSince1970: TimeInterval({}) / 1_000)" 21 | from_custom = "Int64({}.timeIntervalSince1970 * 1_000)" -------------------------------------------------------------------------------- /bindings/prose-sdk-js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | js/*.js 3 | package-lock.json -------------------------------------------------------------------------------- /bindings/prose-sdk-js/README.md: -------------------------------------------------------------------------------- 1 | # prose-sdk-js 2 | 3 | **Wasm/JS bindings for `prose-core-client`.** 4 | 5 | To build this during development of [prose-app-web](https://github.com/prose-im/prose-app-web), in the [package.json](https://github.com/prose-im/prose-app-web/blob/master/package.json) replace the source of `@prose-im/prose-sdk-js` with `"file:/LOCAL/PATH/TO/prose-core-client/master/bindings/prose-sdk-js/pkg`" and run `cargo xtask wasm-pack build --dev` from the root folder of this repo to compile the crate. You may also use the convenience `PROSE_CORE_CLIENT_PATH="../prose-core-client" npm run dev` command, passing the `PROSE_CORE_CLIENT_PATH` environment variable. 6 | 7 | Similarly, you can currently publish this library by running `cargo xtask wasm-pack publish`. Make sure however that `GITHUB_TOKEN` is available as an environment variable and contains a valid personal access token with the `repo` scope enabled. Also, please provide a `NPM_TOKEN` environment variable for NPM publishing purposes. 8 | 9 | _⚠️ Note that building this SDK currently requires the `nightly` Rust compiler._ 10 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "strophe.js": "1.6.x" 4 | }, 5 | "devDependencies": { 6 | "@types/strophe.js": "1.2.x" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/connector/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use strophe_js::{Connector, ProseConnectionProvider}; 7 | 8 | mod strophe_js; 9 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/encryption/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::{anyhow, ensure, Context}; 7 | pub use encryption_service::{EncryptionService, JsEncryptionService}; 8 | use prose_core_client::dtos::{DeviceId, UserId}; 9 | pub use signal_repo::SignalRepo; 10 | pub(self) use signal_repo::*; 11 | 12 | mod encryption_service; 13 | mod js_compat; 14 | mod signal_repo; 15 | 16 | fn try_decode_address(encoded_address: &str) -> anyhow::Result<(UserId, DeviceId)> { 17 | let mut parts = encoded_address.rsplitn(2, '.'); 18 | 19 | let device_id = parts 20 | .next() 21 | .ok_or(anyhow!("Invalid address '{encoded_address}'"))? 22 | .parse::() 23 | .with_context(|| format!("Invalid address '{encoded_address}'"))? 24 | .into(); 25 | let user_id = parts 26 | .next() 27 | .ok_or(anyhow!("Invalid address '{encoded_address}'"))? 28 | .parse()?; 29 | 30 | ensure!( 31 | parts.next().is_none(), 32 | "Invalid address '{encoded_address}'" 33 | ); 34 | Ok((user_id, device_id)) 35 | } 36 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/error.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use wasm_bindgen::JsError; 7 | 8 | pub type Result = std::result::Result; 9 | 10 | #[derive(thiserror::Error, Debug)] 11 | #[error(transparent)] 12 | pub struct WasmError(#[from] anyhow::Error); 13 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/lib.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #![feature(extern_types)] 7 | 8 | // Required for wasm-bindgen-derive 9 | extern crate alloc; 10 | extern crate core; 11 | 12 | use wasm_bindgen::prelude::*; 13 | 14 | mod client; 15 | mod connector; 16 | mod delegate; 17 | mod encryption; 18 | mod error; 19 | mod error_hook; 20 | mod log; 21 | mod types; 22 | 23 | #[wasm_bindgen(start)] 24 | pub fn start() { 25 | error_hook::set_once(); 26 | } 27 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/log/js_logger.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use wasm_bindgen::prelude::wasm_bindgen; 7 | 8 | #[wasm_bindgen(typescript_custom_section)] 9 | const TS_APPEND_CONTENT: &'static str = r#" 10 | export interface ProseLogger { 11 | logDebug(message: string) 12 | logInfo(message: string) 13 | logWarn(message: string) 14 | logError(message: string) 15 | } 16 | "#; 17 | 18 | #[wasm_bindgen] 19 | extern "C" { 20 | #[wasm_bindgen(typescript_type = "ProseLogger")] 21 | pub type JSLogger; 22 | 23 | #[wasm_bindgen(method, js_name = "logDebug")] 24 | pub fn log_debug(this: &JSLogger, msg: &str); 25 | 26 | #[wasm_bindgen(method, js_name = "logInfo")] 27 | pub fn log_info(this: &JSLogger, msg: &str); 28 | 29 | #[wasm_bindgen(method, js_name = "logWarn")] 30 | pub fn log_warn(this: &JSLogger, msg: &str); 31 | 32 | #[wasm_bindgen(method, js_name = "logError")] 33 | pub fn log_error(this: &JSLogger, msg: &str); 34 | 35 | #[wasm_bindgen(method, js_name = "logPanic")] 36 | pub fn log_panic(this: &JSLogger, msg: &str); 37 | } 38 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/log/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use js_log_writer::MakeJSLogWriter; 7 | pub use js_logger::JSLogger; 8 | 9 | mod js_log_writer; 10 | mod js_logger; 11 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/types/account_info.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use wasm_bindgen::prelude::*; 7 | 8 | use prose_core_client::dtos::AccountInfo as CoreAccountInfo; 9 | 10 | use super::{contact::UserStatus, Availability, Avatar, BareJid}; 11 | 12 | #[wasm_bindgen] 13 | pub struct AccountInfo(CoreAccountInfo); 14 | 15 | impl From for AccountInfo { 16 | fn from(value: CoreAccountInfo) -> Self { 17 | AccountInfo(value) 18 | } 19 | } 20 | 21 | #[wasm_bindgen] 22 | impl AccountInfo { 23 | #[wasm_bindgen(getter)] 24 | pub fn jid(&self) -> BareJid { 25 | self.0.id.clone().into_inner().into() 26 | } 27 | 28 | #[wasm_bindgen(getter)] 29 | pub fn name(&self) -> String { 30 | self.0.name.clone() 31 | } 32 | 33 | #[wasm_bindgen(getter)] 34 | pub fn avatar(&self) -> Option { 35 | self.0.avatar.clone().map(|avatar| avatar.into()) 36 | } 37 | 38 | #[wasm_bindgen(getter)] 39 | pub fn availability(&self) -> Availability { 40 | self.0.availability.into() 41 | } 42 | 43 | #[wasm_bindgen(getter)] 44 | pub fn status(&self) -> Option { 45 | self.0 46 | .status 47 | .as_ref() 48 | .map(|activity| activity.clone().into()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/types/channel.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use wasm_bindgen::prelude::wasm_bindgen; 7 | 8 | use prose_core_client::dtos::PublicRoomInfo; 9 | 10 | use crate::types::BareJid; 11 | 12 | #[wasm_bindgen] 13 | pub struct Channel { 14 | jid: BareJid, 15 | name: String, 16 | } 17 | 18 | #[wasm_bindgen] 19 | impl Channel { 20 | #[wasm_bindgen(getter)] 21 | pub fn jid(&self) -> BareJid { 22 | self.jid.clone() 23 | } 24 | 25 | #[wasm_bindgen(getter)] 26 | pub fn name(&self) -> String { 27 | self.name.clone() 28 | } 29 | } 30 | 31 | impl From for Channel { 32 | fn from(value: PublicRoomInfo) -> Self { 33 | let bare_jid = value.id.clone().into_inner(); 34 | 35 | Channel { 36 | jid: bare_jid.clone().into(), 37 | name: value 38 | .name 39 | .or(bare_jid.node().map(|n| n.to_string())) 40 | .unwrap_or(value.id.to_string()), 41 | } 42 | } 43 | } 44 | 45 | #[wasm_bindgen] 46 | extern "C" { 47 | #[wasm_bindgen(typescript_type = "Channel[]")] 48 | pub type ChannelsArray; 49 | } 50 | -------------------------------------------------------------------------------- /bindings/prose-sdk-js/src/types/message_result_set.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-sdk-js 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use wasm_bindgen::prelude::wasm_bindgen; 7 | 8 | use prose_core_client::dtos::MessageResultSet as CoreMessageResultSet; 9 | 10 | use crate::types::MessagesArray; 11 | 12 | #[wasm_bindgen] 13 | pub struct MessageResultSet(CoreMessageResultSet); 14 | 15 | #[wasm_bindgen] 16 | impl MessageResultSet { 17 | /// The requested messages in the order from oldest to newest. 18 | #[wasm_bindgen(getter)] 19 | pub fn messages(self) -> MessagesArray { 20 | self.0.messages.clone().into() 21 | } 22 | 23 | /// Can be used to load more messages. `lastMessageId` might not be contained in `messages`. 24 | /// If not set there are no more messages to load. 25 | #[wasm_bindgen(getter, js_name = "lastMessageId")] 26 | pub fn last_message_id(&self) -> Option { 27 | self.0.last_message_id.clone().map(|id| id.to_string()) 28 | } 29 | } 30 | 31 | impl From for MessageResultSet { 32 | fn from(value: CoreMessageResultSet) -> Self { 33 | Self(value) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/deps/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use app_context::{AppConfig, AppContext}; 7 | pub use app_dependencies::*; 8 | 9 | mod app_context; 10 | mod app_dependencies; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/account_info.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::dtos::{Availability, Avatar, UserId, UserStatus}; 7 | 8 | #[derive(Debug, PartialEq, Clone)] 9 | pub struct AccountInfo { 10 | pub id: UserId, 11 | pub name: String, 12 | pub avatar: Option, 13 | pub availability: Availability, 14 | pub status: Option, 15 | } 16 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/contact.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::contacts::models::PresenceSubscription; 7 | use crate::domain::shared::models::{Availability, UserId}; 8 | use crate::domain::user_info::models::UserStatus; 9 | use crate::dtos::Avatar; 10 | 11 | #[derive(Debug, PartialEq, Clone, Copy)] 12 | pub enum Group { 13 | Team, 14 | Other, 15 | } 16 | 17 | #[derive(Debug, PartialEq, Clone)] 18 | pub struct Contact { 19 | pub id: UserId, 20 | pub name: String, 21 | pub full_name: Option, 22 | pub avatar: Option, 23 | pub availability: Availability, 24 | pub status: Option, 25 | pub group: Group, 26 | pub presence_subscription: PresenceSubscription, 27 | } 28 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/message_result_set.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use super::Message; 7 | use crate::dtos::MessageId; 8 | 9 | #[derive(Debug, PartialEq)] 10 | pub struct MessageResultSet { 11 | /// The requested messages in the order from oldest to newest. 12 | pub messages: Vec, 13 | /// Can be used to load more messages. `last_message_id` might not be contained in `messages`. 14 | /// If not set there are no more messages to load. 15 | pub last_message_id: Option, 16 | } 17 | 18 | impl IntoIterator for MessageResultSet { 19 | type Item = Message; 20 | type IntoIter = std::vec::IntoIter; 21 | 22 | fn into_iter(self) -> Self::IntoIter { 23 | self.messages.into_iter() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/presence_sub_request.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Debug, Display, Formatter}; 7 | 8 | use crate::dtos::UserId; 9 | 10 | #[derive(Debug, Clone, PartialEq)] 11 | pub struct PresenceSubRequest { 12 | pub id: PresenceSubRequestId, 13 | pub name: String, 14 | pub user_id: UserId, 15 | } 16 | 17 | #[derive(Debug, PartialEq, Clone)] 18 | pub struct PresenceSubRequestId(UserId); 19 | 20 | impl From for PresenceSubRequestId { 21 | fn from(value: UserId) -> Self { 22 | Self(value) 23 | } 24 | } 25 | 26 | impl PresenceSubRequestId { 27 | pub(crate) fn to_user_id(&self) -> UserId { 28 | self.0.clone() 29 | } 30 | } 31 | 32 | impl Display for PresenceSubRequestId { 33 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 34 | write!(f, "{}", self.0) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/send_message_request.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use super::{Attachment, Markdown}; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | pub struct SendMessageRequest { 10 | pub body: Option, 11 | pub attachments: Vec, 12 | } 13 | 14 | #[derive(Debug, Clone, PartialEq)] 15 | pub struct Body { 16 | pub text: Markdown, 17 | } 18 | 19 | impl SendMessageRequest { 20 | pub fn is_empty(&self) -> bool { 21 | self.body 22 | .as_ref() 23 | .map(|body| body.text.as_ref().is_empty()) 24 | .unwrap_or(true) 25 | && self.attachments.is_empty() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/sidebar_item.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Debug, Formatter}; 7 | 8 | use crate::dtos::RoomEnvelope; 9 | 10 | #[derive(Clone, PartialEq)] 11 | pub struct SidebarItem { 12 | pub name: String, 13 | pub room: RoomEnvelope, 14 | pub is_favorite: bool, 15 | pub has_draft: bool, 16 | pub unread_count: u32, 17 | pub mentions_count: u32, 18 | } 19 | 20 | impl Debug for SidebarItem { 21 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 22 | f.debug_struct("Client") 23 | .field("id", &self.room.to_generic_room().data.room_id) 24 | .field("name", &self.name) 25 | .field("type", &self.room.to_generic_room().data.r#type) 26 | .field("is_favorite", &self.is_favorite) 27 | .field("has_draft", &self.has_draft) 28 | .field("unread_count", &self.unread_count) 29 | .field("mentions_count", &self.mentions_count) 30 | .finish() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/upload_slot.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use super::UploadHeader; 7 | use mime::Mime; 8 | use url::Url; 9 | 10 | pub struct UploadSlot { 11 | pub upload_url: Url, 12 | pub upload_headers: Vec, 13 | pub download_url: Url, 14 | pub file_name: String, 15 | pub media_type: Mime, 16 | pub file_size: u64, 17 | } 18 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/dtos/workspace_info.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::workspace::models::WorkspaceIcon; 7 | 8 | #[derive(Debug, PartialEq, Clone)] 9 | pub struct WorkspaceInfo { 10 | pub name: String, 11 | pub icon: Option, 12 | pub accent_color: Option, 13 | } 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod dtos; 7 | pub mod services; 8 | 9 | #[cfg(feature = "test")] 10 | pub mod deps; 11 | #[cfg(not(feature = "test"))] 12 | pub(crate) mod deps; 13 | 14 | #[cfg(feature = "test")] 15 | pub mod event_handlers; 16 | #[cfg(not(feature = "test"))] 17 | pub(crate) mod event_handlers; 18 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use account_service::AccountService; 7 | pub use block_list_service::BlockListService; 8 | pub use cache_service::CacheService; 9 | pub use connection_service::ConnectionService; 10 | pub use contact_list_service::ContactListService; 11 | #[cfg(feature = "debug")] 12 | pub use debug_service::DebugService; 13 | pub use preview_service::PreviewService; 14 | pub(crate) use room::RoomInner; 15 | pub use room::{DirectMessage, Generic, Group, PrivateChannel, PublicChannel, Room}; 16 | pub use rooms_service::RoomsService; 17 | pub use sidebar_service::SidebarService; 18 | pub use upload_service::UploadService; 19 | pub use user_data_service::UserDataService; 20 | pub use workspace_service::WorkspaceService; 21 | 22 | mod account_service; 23 | mod block_list_service; 24 | mod cache_service; 25 | mod connection_service; 26 | mod contact_list_service; 27 | #[cfg(feature = "debug")] 28 | mod debug_service; 29 | mod preview_service; 30 | pub(crate) mod room; 31 | mod rooms_service; 32 | mod sidebar_service; 33 | mod upload_service; 34 | mod user_data_service; 35 | mod workspace_service; 36 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/app/services/preview_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use prose_markup::MarkdownParser; 7 | use prose_proc_macros::InjectDependencies; 8 | 9 | #[derive(InjectDependencies)] 10 | pub struct PreviewService {} 11 | 12 | impl PreviewService { 13 | pub fn preview_markdown(&self, markdown: impl AsRef) -> String { 14 | let parser = MarkdownParser::new(markdown.as_ref()); 15 | parser.convert_to_html() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/account/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod services; 7 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/account/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use user_account_service::{UserAccountService, UserProfileFormat}; 7 | 8 | mod user_account_service; 9 | 10 | #[cfg(feature = "test")] 11 | pub mod mocks { 12 | pub use super::user_account_service::MockUserAccountService; 13 | } 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/connection/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod services; 8 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/connection/models/connection_properties.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use chrono::{DateTime, Utc}; 7 | 8 | use crate::domain::shared::models::UserResourceId; 9 | use crate::dtos::DecryptionContext; 10 | 11 | use super::ServerFeatures; 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct ConnectionProperties { 15 | /// The time at which the connection was established 16 | pub connection_timestamp: DateTime, 17 | /// The JID of our connected user. 18 | pub connected_jid: UserResourceId, 19 | /// The features of the server we're connected with. 20 | pub server_features: ServerFeatures, 21 | /// Have we loaded the unread messages for the rooms in our sidebar? 22 | pub rooms_caught_up: bool, 23 | /// Used for collecting used PreKeys when during catch-up. 24 | pub decryption_context: Option, 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/connection/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use connection_properties::ConnectionProperties; 7 | pub use server_features::{HttpUploadService, ServerFeatures}; 8 | 9 | mod connection_properties; 10 | mod server_features; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/connection/models/server_features.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use chrono::TimeDelta; 7 | use jid::BareJid; 8 | 9 | use crate::domain::shared::models::MamVersion; 10 | 11 | #[derive(Default, Debug, Clone)] 12 | pub struct ServerFeatures { 13 | pub muc_service: Option, 14 | pub http_upload_service: Option, 15 | pub mam_version: Option, 16 | /// Does the server support vCard4? 17 | pub vcard4: bool, 18 | /// Does the server support XEP-0398: User Avatar to vCard-Based Avatars Conversion? 19 | pub avatar_pep_vcard_conversion: bool, 20 | /// The offset between our local time and the server's time. 21 | pub server_time_offset: TimeDelta, 22 | } 23 | 24 | #[derive(Debug, Clone)] 25 | pub struct HttpUploadService { 26 | pub host: BareJid, 27 | pub max_file_size: u64, 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/connection/services/connection_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | use secrecy::SecretString; 9 | 10 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 11 | use prose_xmpp::ConnectionError; 12 | 13 | use crate::domain::connection::models::ServerFeatures; 14 | use crate::domain::shared::models::UserResourceId; 15 | 16 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 17 | #[async_trait] 18 | #[cfg_attr(feature = "test", mockall::automock)] 19 | pub trait ConnectionService: SendUnlessWasm + SyncUnlessWasm { 20 | async fn connect( 21 | &self, 22 | jid: &UserResourceId, 23 | password: SecretString, 24 | ) -> Result<(), ConnectionError>; 25 | async fn disconnect(&self); 26 | 27 | async fn set_message_carbons_enabled(&self, is_enabled: bool) -> Result<()>; 28 | async fn load_server_features(&self) -> Result; 29 | } 30 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/connection/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use connection_service::ConnectionService; 7 | 8 | mod connection_service; 9 | 10 | #[cfg(feature = "test")] 11 | pub mod mocks { 12 | pub use super::connection_service::MockConnectionService; 13 | } 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod repos; 8 | pub mod services; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/models/contact.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::UserId; 7 | 8 | #[derive(Debug, PartialEq, Clone)] 9 | pub struct Contact { 10 | pub id: UserId, 11 | pub name: Option, 12 | pub presence_subscription: PresenceSubscription, 13 | } 14 | 15 | #[derive(Debug, PartialEq, Clone, Copy)] 16 | pub enum PresenceSubscription { 17 | // We have requested to subscribe to the contact's presence, but they haven't approved yet. 18 | Requested, 19 | 20 | // Both we and the contact are subscribed to each other's presence. 21 | Mutual, 22 | 23 | // The contact is subscribed to our presence, so they can see our status. 24 | TheyFollow, 25 | 26 | // We are subscribed to the contact's presence, so we can see their status. 27 | WeFollow, 28 | 29 | // There is no presence subscription between us and the contact. 30 | None, 31 | } 32 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use contact::{Contact, PresenceSubscription}; 7 | pub use presence_sub_request::PresenceSubRequest; 8 | 9 | mod contact; 10 | mod presence_sub_request; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/models/presence_sub_request.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::UserId; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | pub struct PresenceSubRequest { 10 | /// The id of the user that wants to subscribe to our presence. 11 | pub user_id: UserId, 12 | /// The nickname of the user that wants to subscribe to our presence. 13 | /// https://xmpp.org/extensions/xep-0172.html#example-3 14 | pub name: Option, 15 | } 16 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/repos/block_list_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::shared::models::{AccountId, UserId}; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait BlockListRepository: SendUnlessWasm + SyncUnlessWasm { 17 | async fn contains(&self, account: &AccountId, user_id: &UserId) -> Result; 18 | async fn get_all(&self, account: &AccountId) -> Result>; 19 | async fn insert(&self, account: &AccountId, user_id: &UserId) -> Result; 20 | async fn delete(&self, account: &AccountId, user_id: &UserId) -> Result; 21 | async fn delete_all(&self, account: &AccountId) -> Result; 22 | 23 | async fn reset_before_reconnect(&self, account: &AccountId) -> Result<()>; 24 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/repos/contact_list_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::contacts::models::{Contact, PresenceSubscription}; 12 | use crate::domain::shared::models::{AccountId, UserId}; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait ContactListRepository: SendUnlessWasm + SyncUnlessWasm { 18 | async fn get_all(&self, account: &AccountId) -> Result>; 19 | 20 | async fn set( 21 | &self, 22 | account: &AccountId, 23 | contact_id: &UserId, 24 | subscription: PresenceSubscription, 25 | ) -> Result; 26 | async fn delete(&self, account: &AccountId, contact_id: &UserId) -> Result; 27 | 28 | async fn reset_before_reconnect(&self, account: &AccountId) -> Result<()>; 29 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 30 | } 31 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/repos/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use block_list_repository::BlockListRepository; 7 | pub use contact_list_repository::ContactListRepository; 8 | pub use presence_sub_requests_repository::PresenceSubRequestsRepository; 9 | 10 | mod block_list_repository; 11 | mod contact_list_repository; 12 | mod presence_sub_requests_repository; 13 | 14 | #[cfg(feature = "test")] 15 | pub mod mocks { 16 | pub use super::block_list_repository::MockBlockListRepository; 17 | pub use super::contact_list_repository::MockContactListRepository; 18 | pub use super::presence_sub_requests_repository::MockPresenceSubRequestsRepository; 19 | } 20 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/repos/presence_sub_requests_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::contacts::models::PresenceSubRequest; 12 | use crate::domain::shared::models::{AccountId, UserId}; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait PresenceSubRequestsRepository: SendUnlessWasm + SyncUnlessWasm { 18 | async fn get_all(&self, account: &AccountId) -> Result>; 19 | /// Returns true when the value was newly inserted. 20 | async fn set(&self, account: &AccountId, request: PresenceSubRequest) -> Result; 21 | /// Returns whether the value was present. 22 | async fn delete(&self, account: &AccountId, user_id: &UserId) -> Result; 23 | 24 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/services/block_list_domain_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::dtos::UserId; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait BlockListDomainService: SendUnlessWasm + SyncUnlessWasm { 17 | async fn load_block_list(&self) -> Result>; 18 | async fn block_user(&self, user_id: &UserId) -> Result<()>; 19 | async fn unblock_user(&self, user_id: &UserId) -> Result<()>; 20 | async fn clear_block_list(&self) -> Result<()>; 21 | 22 | async fn handle_user_blocked(&self, user_id: &UserId) -> Result<()>; 23 | async fn handle_user_unblocked(&self, user_id: &UserId) -> Result<()>; 24 | async fn handle_block_list_cleared(&self) -> Result<()>; 25 | 26 | async fn reset_before_reconnect(&self) -> Result<()>; 27 | async fn clear_cache(&self) -> Result<()>; 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/services/block_list_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::dtos::UserId; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait BlockListService: SendUnlessWasm + SyncUnlessWasm { 17 | async fn load_block_list(&self) -> Result>; 18 | async fn block_user(&self, user_id: &UserId) -> Result<()>; 19 | async fn unblock_user(&self, user_id: &UserId) -> Result<()>; 20 | async fn clear_block_list(&self) -> Result<()>; 21 | } 22 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/services/contact_list_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::contacts::models::Contact; 12 | use crate::dtos::UserId; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait ContactListService: SendUnlessWasm + SyncUnlessWasm { 18 | async fn load_contacts(&self) -> Result>; 19 | async fn add_contact(&self, user_id: &UserId) -> Result<()>; 20 | async fn remove_contact(&self, user_id: &UserId) -> Result<()>; 21 | 22 | async fn subscribe_to_presence(&self, user_id: &UserId) -> Result<()>; 23 | async fn unsubscribe_from_presence(&self, user_id: &UserId) -> Result<()>; 24 | async fn revoke_presence_subscription(&self, user_id: &UserId) -> Result<()>; 25 | async fn preapprove_subscription_request(&self, user_id: &UserId) -> Result<()>; 26 | 27 | async fn approve_presence_sub_request(&self, to: &UserId) -> Result<()>; 28 | async fn deny_presence_sub_request(&self, to: &UserId) -> Result<()>; 29 | } 30 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/services/impls/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use block_list_domain_service::{BlockListDomainService, BlockListDomainServiceDependencies}; 7 | pub use contact_list_domain_service::{ 8 | ContactListDomainService, ContactListDomainServiceDependencies, 9 | }; 10 | 11 | mod block_list_domain_service; 12 | mod contact_list_domain_service; 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/contacts/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use block_list_domain_service::BlockListDomainService; 7 | pub use block_list_service::BlockListService; 8 | pub use contact_list_domain_service::ContactListDomainService; 9 | pub use contact_list_service::ContactListService; 10 | 11 | mod block_list_domain_service; 12 | mod block_list_service; 13 | mod contact_list_domain_service; 14 | mod contact_list_service; 15 | pub mod impls; 16 | 17 | #[cfg(feature = "test")] 18 | pub mod mocks { 19 | pub use super::block_list_domain_service::MockBlockListDomainService; 20 | pub use super::block_list_service::MockBlockListService; 21 | pub use super::contact_list_domain_service::MockContactListDomainService; 22 | pub use super::contact_list_service::MockContactListService; 23 | } 24 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod repos; 8 | pub mod services; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/models/device.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Display, Formatter}; 7 | 8 | use super::DeviceId; 9 | 10 | #[derive(Debug, Clone, PartialEq)] 11 | pub struct Device { 12 | pub id: DeviceId, 13 | pub label: Option, 14 | } 15 | 16 | #[derive(Debug, Clone, PartialEq)] 17 | pub struct DeviceList { 18 | pub devices: Vec, 19 | } 20 | 21 | impl Display for Device { 22 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 23 | if let Some(label) = &self.label { 24 | write!(f, "{} (\"{label}\")", self.id.as_ref()) 25 | } else { 26 | write!(f, "{}", self.id.as_ref()) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/models/device_bundle.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::encryption::models::{DeviceId, IdentityKey, PublicPreKey, PublicSignedPreKey}; 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct DeviceBundle { 10 | pub device_id: DeviceId, 11 | pub signed_pre_key: PublicSignedPreKey, 12 | pub identity_key: IdentityKey, 13 | pub pre_keys: Vec, 14 | } 15 | 16 | #[derive(Clone, Debug)] 17 | pub struct PreKeyBundle { 18 | pub device_id: DeviceId, 19 | pub signed_pre_key: PublicSignedPreKey, 20 | pub identity_key: IdentityKey, 21 | pub pre_key: PublicPreKey, 22 | } 23 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/models/device_info.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use super::{DeviceId, IdentityKey, Trust}; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | pub struct DeviceInfo { 10 | pub id: DeviceId, 11 | pub identity: IdentityKey, 12 | pub trust: Trust, 13 | pub is_active: bool, 14 | pub is_this_device: bool, 15 | } 16 | 17 | impl DeviceInfo { 18 | pub fn fingerprint(&self) -> String { 19 | self.identity.fingerprint() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/models/local_device.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::encryption::models::{DeviceId, IdentityKeyPair}; 7 | 8 | #[derive(Clone, Debug)] 9 | pub struct LocalDevice { 10 | pub device_id: DeviceId, 11 | pub identity_key_pair: IdentityKeyPair, 12 | } 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/models/local_encryption_bundle.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::encryption::models::{ 7 | DeviceBundle, DeviceId, IdentityKeyPair, PreKey, SignedPreKey, 8 | }; 9 | 10 | #[derive(Clone, Debug)] 11 | pub struct LocalEncryptionBundle { 12 | pub device_id: DeviceId, 13 | pub identity_key_pair: IdentityKeyPair, 14 | pub signed_pre_key: SignedPreKey, 15 | pub pre_keys: Vec, 16 | } 17 | 18 | impl LocalEncryptionBundle { 19 | pub fn into_device_bundle(self) -> DeviceBundle { 20 | DeviceBundle { 21 | device_id: self.device_id, 22 | signed_pre_key: self.signed_pre_key.into_public_signed_pre_key(), 23 | identity_key: self.identity_key_pair.identity_key, 24 | pre_keys: self 25 | .pre_keys 26 | .into_iter() 27 | .map(|key| key.into_public_pre_key()) 28 | .collect(), 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use decryption_context::{DecryptionContext, DecryptionContextInner}; 7 | pub use device::{Device, DeviceList}; 8 | pub use device_bundle::{DeviceBundle, PreKeyBundle}; 9 | pub use device_id::DeviceId; 10 | pub use device_info::DeviceInfo; 11 | pub use keys::*; 12 | pub use local_device::LocalDevice; 13 | pub use local_encryption_bundle::LocalEncryptionBundle; 14 | pub use session::{Session, Trust}; 15 | 16 | mod decryption_context; 17 | mod device; 18 | mod device_bundle; 19 | mod device_id; 20 | mod device_info; 21 | mod keys; 22 | mod local_device; 23 | mod local_encryption_bundle; 24 | mod session; 25 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/models/session.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::domain::shared::models::UserId; 9 | 10 | use super::{DeviceId, IdentityKey, SessionData}; 11 | 12 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] 13 | pub enum Trust { 14 | Undecided, 15 | Untrusted, 16 | Trusted, 17 | Verified, 18 | } 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct Session { 22 | pub user_id: UserId, 23 | pub device_id: DeviceId, 24 | pub trust: Trust, 25 | pub is_active: bool, 26 | pub identity: Option, 27 | pub data: Option, 28 | } 29 | 30 | impl Session { 31 | pub fn is_trusted(&self) -> bool { 32 | match self.trust { 33 | Trust::Untrusted => false, 34 | Trust::Undecided => false, 35 | Trust::Trusted | Trust::Verified => true, 36 | } 37 | } 38 | 39 | pub fn is_trusted_or_undecided(&self) -> bool { 40 | self.is_trusted() || self.trust == Trust::Undecided 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/repos/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use encryption_keys_repository::EncryptionKeysRepository; 7 | pub use session_repository::SessionRepository; 8 | pub use user_device_repository::UserDeviceRepository; 9 | 10 | pub mod encryption_keys_repository; 11 | mod session_repository; 12 | mod user_device_repository; 13 | 14 | #[cfg(feature = "test")] 15 | pub mod mocks { 16 | pub use super::encryption_keys_repository::MockEncryptionKeysRepository; 17 | pub use super::session_repository::MockSessionRepository; 18 | pub use super::user_device_repository::MockUserDeviceRepository; 19 | } 20 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/repos/user_device_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::encryption::models::Device; 12 | use crate::domain::shared::models::AccountId; 13 | use crate::dtos::UserId; 14 | 15 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 16 | #[async_trait] 17 | #[cfg_attr(feature = "test", mockall::automock)] 18 | pub trait UserDeviceRepository: SendUnlessWasm + SyncUnlessWasm { 19 | /// Returns all devices associated with `user_id`. 20 | async fn get_all(&self, account: &AccountId, user_id: &UserId) -> Result>; 21 | /// Sets `devices` for `user_id`. Devices not contained in `devices` will be deleted. 22 | async fn set_all( 23 | &self, 24 | account: &AccountId, 25 | user_id: &UserId, 26 | devices: Vec, 27 | ) -> Result<()>; 28 | 29 | async fn reset_before_reconnect(&self, account: &AccountId) -> Result<()>; 30 | /// Deletes all cached devices for all users. 31 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 32 | } 33 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/services/impls/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use encryption_domain_service::{EncryptionDomainService, EncryptionDomainServiceDependencies}; 7 | 8 | mod encryption_domain_service; 9 | 10 | #[cfg(not(target_arch = "wasm32"))] 11 | pub mod signal_native; 12 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/services/impls/signal_native/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use signal_service::SignalServiceHandle; 7 | 8 | pub(self) use signal_repo_wrapper::SignalRepoWrapper; 9 | 10 | mod signal_compat; 11 | mod signal_repo_wrapper; 12 | mod signal_service; 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use encryption_domain_service::{DecryptionError, EncryptionDomainService, EncryptionError}; 7 | pub use encryption_service::EncryptionService; 8 | pub use user_device_id_provider::{RandUserDeviceIdProvider, UserDeviceIdProvider}; 9 | pub use user_device_service::UserDeviceService; 10 | 11 | mod encryption_domain_service; 12 | mod encryption_service; 13 | pub mod impls; 14 | mod user_device_id_provider; 15 | mod user_device_service; 16 | 17 | #[cfg(feature = "test")] 18 | pub use user_device_id_provider::IncrementingUserDeviceIdProvider; 19 | 20 | #[cfg(feature = "test")] 21 | pub mod mocks { 22 | pub use super::encryption_domain_service::MockEncryptionDomainService; 23 | pub use super::encryption_service::MockEncryptionService; 24 | pub use super::user_device_service::MockUserDeviceService; 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/services/user_device_id_provider.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use parking_lot::Mutex; 7 | use rand::Rng; 8 | 9 | use crate::domain::encryption::models::DeviceId; 10 | 11 | pub trait UserDeviceIdProvider: Send + Sync { 12 | fn new_id(&self) -> DeviceId; 13 | } 14 | 15 | #[derive(Default)] 16 | pub struct RandUserDeviceIdProvider {} 17 | 18 | impl UserDeviceIdProvider for RandUserDeviceIdProvider { 19 | fn new_id(&self) -> DeviceId { 20 | DeviceId::from(rand::thread_rng().gen_range(1..2u32.pow(31))) 21 | } 22 | } 23 | 24 | pub struct IncrementingUserDeviceIdProvider { 25 | last_id: Mutex, 26 | } 27 | 28 | impl IncrementingUserDeviceIdProvider { 29 | #[allow(dead_code)] 30 | pub fn new(value: u32) -> Self { 31 | IncrementingUserDeviceIdProvider { 32 | last_id: Mutex::new(value), 33 | } 34 | } 35 | } 36 | 37 | impl UserDeviceIdProvider for IncrementingUserDeviceIdProvider { 38 | fn new_id(&self) -> DeviceId { 39 | let mut last_id = self.last_id.lock(); 40 | let id = DeviceId::from(*last_id); 41 | *last_id += 1; 42 | id 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/encryption/services/user_device_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::encryption::models::{DeviceBundle, DeviceId, DeviceList}; 12 | use crate::domain::shared::models::UserId; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait UserDeviceService: SendUnlessWasm + SyncUnlessWasm { 18 | async fn load_device_list(&self, user_id: &UserId) -> Result; 19 | async fn publish_device_list(&self, device_list: DeviceList) -> Result<()>; 20 | async fn delete_device_list(&self) -> Result<()>; 21 | 22 | async fn load_device_bundle( 23 | &self, 24 | user_id: &UserId, 25 | device_id: &DeviceId, 26 | ) -> Result>; 27 | async fn publish_device_bundle(&self, bundle: DeviceBundle) -> Result<()>; 28 | async fn delete_device_bundle(&self, device_id: &DeviceId) -> Result<()>; 29 | } 30 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/general/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod services; 8 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/general/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use capabilities::{Capabilities, Feature, Identity}; 7 | pub use software_version::SoftwareVersion; 8 | 9 | mod capabilities; 10 | mod software_version; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/general/models/software_version.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub struct SoftwareVersion { 8 | pub name: String, 9 | pub version: String, 10 | pub os: Option, 11 | } 12 | 13 | impl Default for SoftwareVersion { 14 | fn default() -> Self { 15 | SoftwareVersion { 16 | name: env!("CARGO_PKG_NAME").to_string(), 17 | version: env!("CARGO_PKG_VERSION").to_string(), 18 | os: None, 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/general/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use request_handling_service::RequestHandlingService; 7 | 8 | mod request_handling_service; 9 | 10 | #[cfg(feature = "test")] 11 | pub mod mocks { 12 | pub use super::request_handling_service::MockRequestHandlingService; 13 | } 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod repos; 8 | pub mod services; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/models/attachment.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use mime::Mime; 7 | use serde::{Deserialize, Serialize}; 8 | use url::Url; 9 | 10 | use crate::util::mime_serde_shim; 11 | 12 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 13 | pub struct Attachment { 14 | pub r#type: AttachmentType, 15 | pub url: Url, 16 | #[serde(with = "mime_serde_shim")] 17 | pub media_type: Mime, 18 | pub file_name: String, 19 | pub file_size: Option, 20 | } 21 | 22 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 23 | pub enum AttachmentType { 24 | Audio { 25 | duration: Option, 26 | }, 27 | Image { 28 | thumbnail: Option, 29 | }, 30 | Video { 31 | duration: Option, 32 | thumbnail: Option, 33 | }, 34 | File, 35 | } 36 | 37 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 38 | pub struct Thumbnail { 39 | pub url: Url, 40 | #[serde(with = "mime_serde_shim")] 41 | pub media_type: Mime, 42 | pub width: Option, 43 | pub height: Option, 44 | } 45 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/models/mention.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::ops::Range; 7 | 8 | use serde::{Deserialize, Serialize}; 9 | 10 | use crate::domain::shared::models::UnicodeScalarIndex; 11 | use crate::dtos::UserId; 12 | 13 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 14 | pub struct Mention { 15 | pub user: UserId, 16 | pub range: Option>, 17 | } 18 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/models/message_ref.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use chrono::{DateTime, Utc}; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use super::{MessageRemoteId, MessageServerId}; 10 | 11 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 12 | pub struct ArchivedMessageRef { 13 | pub stanza_id: MessageServerId, 14 | pub timestamp: DateTime, 15 | } 16 | 17 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 18 | pub struct MessageRef { 19 | pub id: MessageRemoteId, 20 | pub timestamp: DateTime, 21 | } 22 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use attachment::{Attachment, AttachmentType, Thumbnail}; 7 | pub use encrypted_message::{ 8 | EncryptedMessage, EncryptedPayload, EncryptionKey, KeyTransportPayload, 9 | }; 10 | pub(crate) use error::StanzaParseError; 11 | pub use mention::Mention; 12 | #[allow(unused_imports)] // Reaction is required in unit tests 13 | pub use message::{Body, Emoji, Message, MessageFlags, Reaction, ReplyTo}; 14 | pub use message_id::{ 15 | MessageId, MessageIdTriple, MessageRemoteId, MessageServerId, MessageTargetId, 16 | }; 17 | pub use message_like::{ 18 | Body as MessageLikeBody, EncryptionInfo as MessageLikeEncryptionInfo, MessageLike, 19 | Payload as MessageLikePayload, 20 | }; 21 | pub use message_parser::{MessageLikeError, MessageParser}; 22 | pub use message_ref::{ArchivedMessageRef, MessageRef}; 23 | pub use send_message_request::SendMessageRequest; 24 | 25 | mod attachment; 26 | mod encrypted_message; 27 | mod error; 28 | mod mention; 29 | mod message; 30 | mod message_id; 31 | mod message_like; 32 | mod message_parser; 33 | mod message_ref; 34 | pub mod send_message_request; 35 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/models/send_message_request.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::{Markdown, StyledMessage}; 7 | 8 | use super::{Attachment, Mention}; 9 | use super::{EncryptedPayload, MessageId}; 10 | 11 | #[derive(Debug, Clone, PartialEq)] 12 | pub struct SendMessageRequest { 13 | pub id: MessageId, 14 | pub body: Option, 15 | pub attachments: Vec, 16 | } 17 | 18 | #[derive(Debug, Clone, PartialEq)] 19 | pub struct Body { 20 | pub payload: Payload, 21 | pub mentions: Vec, 22 | } 23 | 24 | #[derive(Debug, Clone, PartialEq)] 25 | pub enum Payload { 26 | Unencrypted { 27 | message: Markdown, 28 | fallback: StyledMessage, 29 | }, 30 | Encrypted(EncryptedPayload), 31 | } 32 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/repos/drafts_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::shared::models::AccountId; 12 | use crate::dtos::RoomId; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait DraftsRepository: SendUnlessWasm + SyncUnlessWasm { 18 | async fn get(&self, account: &AccountId, room_id: &RoomId) -> Result>; 19 | async fn set(&self, account: &AccountId, room_id: &RoomId, draft: Option<&str>) -> Result<()>; 20 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 21 | } 22 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/repos/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use drafts_repository::DraftsRepository; 7 | pub use messages_repository::MessagesRepository; 8 | pub use offline_messages_repository::OfflineMessagesRepository; 9 | 10 | mod drafts_repository; 11 | mod messages_repository; 12 | mod offline_messages_repository; 13 | 14 | #[cfg(feature = "test")] 15 | pub mod mocks { 16 | pub use super::drafts_repository::MockDraftsRepository; 17 | pub use super::messages_repository::MockMessagesRepository; 18 | pub use super::offline_messages_repository::MockOfflineMessagesRepository; 19 | } 20 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/repos/offline_messages_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 7 | 8 | use crate::app::event_handlers::MessageEvent; 9 | 10 | #[cfg_attr(feature = "test", mockall::automock)] 11 | pub trait OfflineMessagesRepository: SendUnlessWasm + SyncUnlessWasm { 12 | fn push(&self, event: MessageEvent); 13 | fn drain(&self) -> Vec; 14 | } 15 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/services/impls/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use message_archive_domain_service::{ 7 | MessageArchiveDomainService, MessageArchiveDomainServiceDependencies, 8 | }; 9 | pub use message_migration_domain_service::{ 10 | MessageMigrationDomainService, MessageMigrationDomainServiceDependencies, 11 | }; 12 | 13 | mod message_archive_domain_service; 14 | mod message_migration_domain_service; 15 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/services/message_archive_domain_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::encryption::models::DecryptionContext; 7 | use anyhow::Result; 8 | use async_trait::async_trait; 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::rooms::models::Room; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait MessageArchiveDomainService: SendUnlessWasm + SyncUnlessWasm { 17 | /// Returns `true` if new messages were found. 18 | async fn catchup_room(&self, room: &Room, context: DecryptionContext) -> Result; 19 | } 20 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/services/message_id_provider.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::dtos::MessageId; 7 | use prose_xmpp::{IDProvider, UUIDProvider}; 8 | 9 | pub trait MessageIdProvider: Send + Sync { 10 | fn new_id(&self) -> MessageId; 11 | } 12 | 13 | pub struct WrappingMessageIdProvider { 14 | id_provider: T, 15 | } 16 | 17 | impl WrappingMessageIdProvider { 18 | pub fn new(id_provider: T) -> Self { 19 | Self { id_provider } 20 | } 21 | } 22 | 23 | impl WrappingMessageIdProvider { 24 | pub fn uuid() -> Self { 25 | Self { 26 | id_provider: UUIDProvider::new(), 27 | } 28 | } 29 | } 30 | 31 | #[cfg(feature = "test")] 32 | impl WrappingMessageIdProvider { 33 | pub fn incrementing(prefix: &str) -> Self { 34 | Self { 35 | id_provider: prose_xmpp::test::IncrementingIDProvider::new(prefix), 36 | } 37 | } 38 | } 39 | 40 | impl MessageIdProvider for WrappingMessageIdProvider { 41 | fn new_id(&self) -> MessageId { 42 | MessageId::from(self.id_provider.new_id()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/services/message_migration_domain_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::shared::models::RoomId; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait MessageMigrationDomainService: SendUnlessWasm + SyncUnlessWasm { 17 | async fn copy_all_messages_from_room( 18 | &self, 19 | source_room: &RoomId, 20 | target_room: &RoomId, 21 | ) -> Result<()>; 22 | } 23 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/messaging/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use message_archive_domain_service::MessageArchiveDomainService; 7 | pub use message_archive_service::{MessageArchiveService, MessagePage}; 8 | pub use message_id_provider::{MessageIdProvider, WrappingMessageIdProvider}; 9 | pub use message_migration_domain_service::MessageMigrationDomainService; 10 | pub use messaging_service::MessagingService; 11 | 12 | pub mod impls; 13 | mod message_archive_domain_service; 14 | mod message_archive_service; 15 | mod message_id_provider; 16 | mod message_migration_domain_service; 17 | mod messaging_service; 18 | 19 | #[cfg(feature = "test")] 20 | pub mod mocks { 21 | pub use super::message_archive_domain_service::MockMessageArchiveDomainService; 22 | pub use super::message_archive_service::MockMessageArchiveService; 23 | pub use super::message_migration_domain_service::MockMessageMigrationDomainService; 24 | pub use super::messaging_service::MockMessagingService; 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod account; 7 | pub mod connection; 8 | pub mod contacts; 9 | pub mod encryption; 10 | pub mod general; 11 | pub mod messaging; 12 | pub mod rooms; 13 | pub mod settings; 14 | pub mod shared; 15 | pub mod sidebar; 16 | pub mod uploads; 17 | pub mod user_info; 18 | pub mod workspace; 19 | 20 | mod models {} 21 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod repos; 8 | pub mod services; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/models/compose_state.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Emmanuel Gil Peyrot 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | #[derive(Debug, PartialEq, Clone, Default)] 8 | pub enum ComposeState { 9 | #[default] 10 | Idle, 11 | Composing, 12 | } 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/models/constants.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub const MAX_PARTICIPANTS_PER_GROUP: usize = 9; 7 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use compose_state::ComposeState; 7 | pub use participant_list::{Participant, ParticipantList, ParticipantName, RegisteredMember}; 8 | pub use public_room_info::PublicRoomInfo; 9 | pub use room::{Room, RoomInfo, RoomSidebarState, RoomState}; 10 | pub use room_affiliation::RoomAffiliation; 11 | pub use room_error::RoomError; 12 | pub use room_features::RoomFeatures; 13 | pub use room_session_info::{ 14 | RoomConfig, RoomSessionInfo, RoomSessionMember, RoomSessionParticipant, 15 | }; 16 | pub use room_spec::RoomSpec; 17 | 18 | mod compose_state; 19 | pub mod constants; 20 | mod participant_list; 21 | mod public_room_info; 22 | mod room; 23 | mod room_affiliation; 24 | mod room_error; 25 | mod room_features; 26 | mod room_session_info; 27 | mod room_spec; 28 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/models/public_room_info.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::MucId; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | pub struct PublicRoomInfo { 10 | pub id: MucId, 11 | pub name: Option, 12 | } 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/models/room_features.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use chrono::{DateTime, TimeDelta, Utc}; 7 | 8 | use crate::domain::shared::models::MamVersion; 9 | 10 | #[derive(Debug, Clone, PartialEq, Default)] 11 | pub struct RoomFeatures { 12 | /// Which MAM version does the room support, if any? 13 | pub mam_version: Option, 14 | pub server_time_offset: TimeDelta, 15 | /// Does the server support XEP-0410 (MUC Self-Ping)? 16 | pub self_ping_optimization: bool, 17 | } 18 | 19 | impl RoomFeatures { 20 | pub fn is_mam_supported(&self) -> bool { 21 | self.mam_version.is_some() 22 | } 23 | 24 | pub fn local_time_to_server_time(&self, local_time: DateTime) -> DateTime { 25 | local_time + self.server_time_offset 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/repos/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use connected_rooms_repository::{ 7 | ConnectedRoomsReadOnlyRepository, ConnectedRoomsRepository, RoomAlreadyExistsError, 8 | }; 9 | 10 | mod connected_rooms_repository; 11 | 12 | #[cfg(feature = "test")] 13 | pub mod mocks { 14 | pub use super::connected_rooms_repository::MockConnectedRoomsReadOnlyRepository; 15 | pub use super::connected_rooms_repository::MockConnectedRoomsReadWriteRepository; 16 | } 17 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/services/impls/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use room_utils::{build_nickname, ParticipantsVecExt}; 7 | pub use rooms_domain_service::{RoomsDomainService, RoomsDomainServiceDependencies}; 8 | 9 | mod room_utils; 10 | mod rooms_domain_service; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use room_attributes_service::RoomAttributesService; 7 | pub use room_factory::RoomFactory; 8 | pub use room_management_service::RoomManagementService; 9 | pub use room_participation_service::RoomParticipationService; 10 | pub use rooms_domain_service::{ 11 | CreateOrEnterRoomRequest, CreateRoomBehavior, CreateRoomType, JoinRoomBehavior, 12 | JoinRoomFailureBehavior, JoinRoomRedirectBehavior, RoomsDomainService, 13 | }; 14 | 15 | pub mod impls; 16 | mod room_attributes_service; 17 | mod room_factory; 18 | mod room_management_service; 19 | mod room_participation_service; 20 | mod rooms_domain_service; 21 | 22 | #[cfg(feature = "test")] 23 | pub mod mocks { 24 | pub use super::room_attributes_service::MockRoomAttributesService; 25 | pub use super::room_management_service::MockRoomManagementService; 26 | pub use super::room_participation_service::MockRoomParticipationService; 27 | pub use super::rooms_domain_service::MockRoomsDomainService; 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/services/room_attributes_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::shared::models::MucId; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait RoomAttributesService: SendUnlessWasm + SyncUnlessWasm { 17 | async fn set_topic(&self, room_id: &MucId, subject: Option<&str>) -> Result<()>; 18 | async fn set_name(&self, room_id: &MucId, name: &str) -> Result<()>; 19 | } 20 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/rooms/services/room_participation_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use async_trait::async_trait; 7 | 8 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 9 | 10 | use crate::domain::rooms::models::RoomError; 11 | use crate::domain::shared::models::{MucId, UserId}; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait RoomParticipationService: SendUnlessWasm + SyncUnlessWasm { 17 | async fn invite_users_to_room( 18 | &self, 19 | room_id: &MucId, 20 | participants: &[UserId], 21 | ) -> Result<(), RoomError>; 22 | 23 | async fn grant_membership( 24 | &self, 25 | room_id: &MucId, 26 | participant: &UserId, 27 | ) -> Result<(), RoomError>; 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod repos; 8 | pub mod services; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/models/account_settings.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::Availability; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 10 | pub struct AccountSettings { 11 | /// The last configured availability 12 | pub availability: Availability, 13 | /// The generated resource string use to form a FullJid 14 | pub resource: Option, 15 | } 16 | 17 | impl Default for AccountSettings { 18 | fn default() -> Self { 19 | AccountSettings { 20 | availability: Availability::Available, 21 | resource: None, 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/models/local_room_settings.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use chrono::{DateTime, Utc}; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Debug, Default, Serialize, Deserialize, PartialEq)] 10 | pub struct LocalRoomSettings { 11 | pub last_catchup_time: Option>, 12 | } 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use account_settings::AccountSettings; 7 | pub use local_room_settings::LocalRoomSettings; 8 | pub use synced_room_settings::SyncedRoomSettings; 9 | 10 | mod account_settings; 11 | mod local_room_settings; 12 | mod synced_room_settings; 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/models/synced_room_settings.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::domain::messaging::models::ArchivedMessageRef; 9 | use crate::dtos::RoomId; 10 | 11 | #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 12 | pub struct SyncedRoomSettings { 13 | pub room_id: RoomId, 14 | pub encryption_enabled: bool, 15 | pub last_read_message: Option, 16 | } 17 | 18 | impl SyncedRoomSettings { 19 | pub fn new(room_id: RoomId) -> Self { 20 | Self { 21 | room_id, 22 | encryption_enabled: false, 23 | last_read_message: Default::default(), 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/repos/account_settings_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::settings::models::AccountSettings; 12 | use crate::domain::shared::models::AccountId; 13 | 14 | type UpdateHandler = Box FnOnce(&'a mut AccountSettings) + Send>; 15 | 16 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 17 | #[async_trait] 18 | #[cfg_attr(feature = "test", mockall::automock)] 19 | pub trait AccountSettingsRepository: SendUnlessWasm + SyncUnlessWasm { 20 | async fn get(&self, account: &AccountId) -> Result; 21 | async fn update(&self, account: &AccountId, block: UpdateHandler) -> Result<()>; 22 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 23 | } 24 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/repos/local_room_settings_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::settings::models::LocalRoomSettings; 12 | use crate::domain::shared::models::{AccountId, RoomId}; 13 | 14 | type UpdateHandler = Box FnOnce(&'a mut LocalRoomSettings) + Send>; 15 | 16 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 17 | #[async_trait] 18 | #[cfg_attr(feature = "test", mockall::automock)] 19 | pub trait LocalRoomSettingsRepository: SendUnlessWasm + SyncUnlessWasm { 20 | async fn get(&self, account: &AccountId, room_id: &RoomId) -> Result; 21 | async fn update( 22 | &self, 23 | account: &AccountId, 24 | room_id: &RoomId, 25 | block: UpdateHandler, 26 | ) -> Result<()>; 27 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/repos/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use account_settings_repository::AccountSettingsRepository; 7 | pub use local_room_settings_repository::LocalRoomSettingsRepository; 8 | 9 | mod account_settings_repository; 10 | mod local_room_settings_repository; 11 | 12 | #[cfg(feature = "test")] 13 | pub mod mocks { 14 | pub use super::account_settings_repository::MockAccountSettingsRepository; 15 | pub use super::local_room_settings_repository::MockLocalRoomSettingsRepository; 16 | } 17 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use synced_room_settings_service::SyncedRoomSettingsService; 7 | 8 | mod synced_room_settings_service; 9 | 10 | #[cfg(feature = "test")] 11 | pub mod mocks { 12 | pub use super::synced_room_settings_service::MockSyncedRoomSettingsService; 13 | } 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/settings/services/synced_room_settings_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::settings::models::SyncedRoomSettings; 12 | use crate::domain::shared::models::RoomId; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait SyncedRoomSettingsService: SendUnlessWasm + SyncUnlessWasm { 18 | async fn load_settings(&self, room_id: &RoomId) -> Result>; 19 | async fn save_settings(&self, room_id: &RoomId, settings: &SyncedRoomSettings) -> Result<()>; 20 | } 21 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod utils; 8 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/anon_occupant_id.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Debug, Display, Formatter}; 7 | 8 | #[derive(Clone, PartialEq, Eq, Hash)] 9 | /// Represents an anonymous identifier of a user within a Multi-User Chat (MUC) room. 10 | /// See: https://xmpp.org/extensions/xep-0421.html 11 | pub struct AnonOccupantId(String); 12 | 13 | impl From for AnonOccupantId 14 | where 15 | T: Into, 16 | { 17 | fn from(s: T) -> Self { 18 | AnonOccupantId(s.into()) 19 | } 20 | } 21 | 22 | impl Display for AnonOccupantId { 23 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 24 | write!(f, "{}", self.0) 25 | } 26 | } 27 | 28 | impl Debug for AnonOccupantId { 29 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 30 | write!(f, "AnonOccupantId({})", self.0) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/availability.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use serde::{Deserialize, Serialize}; 7 | use std::fmt::{Display, Formatter}; 8 | 9 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Copy)] 10 | pub enum Availability { 11 | Available, 12 | #[default] 13 | Unavailable, 14 | DoNotDisturb, 15 | Away, 16 | Invisible, 17 | } 18 | 19 | impl Display for Availability { 20 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 21 | write!( 22 | f, 23 | "{}", 24 | match self { 25 | Availability::Available => "available", 26 | Availability::Unavailable => "unavailable", 27 | Availability::DoNotDisturb => "do not disturb", 28 | Availability::Away => "away", 29 | Availability::Invisible => "invisible", 30 | } 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/avatar_id.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Display, Formatter}; 7 | use std::str::FromStr; 8 | 9 | use anyhow::{ensure, Result}; 10 | use serde::{Deserialize, Serialize}; 11 | 12 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 13 | #[serde(transparent)] 14 | pub struct AvatarId(String); 15 | 16 | impl AvatarId { 17 | pub fn from_str_unchecked(s: impl Into) -> Self { 18 | Self(s.into()) 19 | } 20 | } 21 | 22 | impl FromStr for AvatarId { 23 | type Err = anyhow::Error; 24 | 25 | fn from_str(s: &str) -> Result { 26 | ensure!(is_valid_sha1(s), "Avatar ID is not a valid SHA1 hash."); 27 | Ok(Self(s.to_string())) 28 | } 29 | } 30 | 31 | impl Display for AvatarId { 32 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 33 | f.write_str(&self.0) 34 | } 35 | } 36 | 37 | fn is_valid_sha1(s: &str) -> bool { 38 | s.len() == 40 && s.chars().all(|c| c.is_digit(16)) 39 | } 40 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/avatar_metadata.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::domain::shared::models::AvatarId; 9 | 10 | #[derive(Clone, PartialEq, Debug)] 11 | pub struct AvatarMetadata { 12 | pub bytes: usize, 13 | pub mime_type: String, 14 | pub checksum: AvatarId, 15 | pub width: Option, 16 | pub height: Option, 17 | pub url: Option, 18 | } 19 | 20 | #[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] 21 | pub struct AvatarInfo { 22 | pub checksum: AvatarId, 23 | pub mime_type: String, 24 | } 25 | 26 | impl AvatarMetadata { 27 | pub fn to_info(&self) -> AvatarInfo { 28 | AvatarInfo { 29 | checksum: self.checksum.clone(), 30 | mime_type: self.mime_type.clone(), 31 | } 32 | } 33 | 34 | pub fn into_info(self) -> AvatarInfo { 35 | AvatarInfo { 36 | checksum: self.checksum, 37 | mime_type: self.mime_type, 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/cache_policy.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #[derive(Debug, PartialEq, Copy, Clone, Default)] 7 | pub enum CachePolicy { 8 | #[default] 9 | ReturnCacheDataElseLoad, 10 | ReturnCacheDataDontLoad, 11 | } 12 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/capabilities_id.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Display, Formatter}; 7 | 8 | /// `CapabilitiesId` represents an XMPP capabilities node identifier, concatenating a 'node' URL 9 | /// and a 'ver' version string, separated by '#'. 10 | /// https://xmpp.org/extensions/xep-0115.html 11 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 12 | pub struct CapabilitiesId(String); 13 | 14 | impl CapabilitiesId { 15 | pub fn new(node: impl AsRef, ver: impl AsRef) -> Self { 16 | Self(format!("{}#{}", node.as_ref(), ver.as_ref())) 17 | } 18 | } 19 | 20 | impl From for CapabilitiesId 21 | where 22 | T: Into, 23 | { 24 | fn from(s: T) -> Self { 25 | CapabilitiesId(s.into()) 26 | } 27 | } 28 | 29 | impl AsRef for CapabilitiesId { 30 | fn as_ref(&self) -> &str { 31 | &self.0 32 | } 33 | } 34 | 35 | impl Display for CapabilitiesId { 36 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 37 | f.write_str(self.as_ref()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/connection_state.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] 7 | pub enum ConnectionState { 8 | #[default] 9 | Disconnected, 10 | Connecting, 11 | Connected, 12 | } 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/mam_version.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)] 7 | pub enum MamVersion { 8 | Mam0, 9 | Mam1, 10 | Mam2, 11 | Mam2Extended, 12 | } 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/request_id.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Display, Formatter}; 7 | 8 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 9 | /// Represents the identifier of a request directed at us. 10 | pub struct RequestId(String); 11 | 12 | impl From for RequestId 13 | where 14 | T: Into, 15 | { 16 | fn from(s: T) -> Self { 17 | RequestId(s.into()) 18 | } 19 | } 20 | 21 | impl Display for RequestId { 22 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 23 | write!(f, "{}", self.0) 24 | } 25 | } 26 | 27 | impl AsRef for RequestId { 28 | fn as_ref(&self) -> &str { 29 | &self.0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/sender_id.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::fmt::{Debug, Display, Formatter}; 7 | 8 | use jid::Jid; 9 | 10 | #[derive(Clone, PartialEq, Eq, Hash)] 11 | /// Represents a unspecified XMPP identifier. Could be a user, server, user resource, etc.… 12 | pub struct SenderId(Jid); 13 | 14 | impl SenderId { 15 | pub fn into_inner(self) -> Jid { 16 | self.0 17 | } 18 | } 19 | 20 | impl From for SenderId 21 | where 22 | T: Into, 23 | { 24 | fn from(value: T) -> Self { 25 | SenderId(value.into()) 26 | } 27 | } 28 | 29 | impl Debug for SenderId { 30 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 31 | write!(f, "SenderId({})", self.0) 32 | } 33 | } 34 | 35 | impl Display for SenderId { 36 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 37 | write!(f, "{}", self.0) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/models/user_or_resource_id.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::{UserId, UserResourceId}; 7 | 8 | #[derive(Debug, Clone, PartialEq, Hash)] 9 | pub enum UserOrResourceId { 10 | User(UserId), 11 | UserResource(UserResourceId), 12 | } 13 | 14 | impl UserOrResourceId { 15 | pub fn to_user_id(&self) -> UserId { 16 | match self { 17 | UserOrResourceId::User(id) => id.clone(), 18 | UserOrResourceId::UserResource(id) => id.to_user_id(), 19 | } 20 | } 21 | 22 | pub fn resource_str(&self) -> Option<&str> { 23 | match self { 24 | UserOrResourceId::User(_) => None, 25 | UserOrResourceId::UserResource(id) => Some(id.resource()), 26 | } 27 | } 28 | } 29 | 30 | impl From for UserOrResourceId { 31 | fn from(value: UserId) -> Self { 32 | Self::User(value) 33 | } 34 | } 35 | 36 | impl From for UserOrResourceId { 37 | fn from(value: UserResourceId) -> Self { 38 | Self::UserResource(value) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/shared/utils/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use contact_name_builder::ContactNameBuilder; 7 | 8 | mod contact_name_builder; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/sidebar/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod services; 8 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/sidebar/models/bookmark.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::rooms::models::RoomSidebarState; 7 | use crate::domain::shared::models::RoomId; 8 | 9 | use super::BookmarkType; 10 | 11 | #[derive(Debug, Clone, PartialEq)] 12 | pub struct Bookmark { 13 | pub name: String, 14 | pub jid: RoomId, 15 | pub r#type: BookmarkType, 16 | pub sidebar_state: RoomSidebarState, 17 | } 18 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/sidebar/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use bookmark::Bookmark; 7 | pub use bookmark_type::BookmarkType; 8 | 9 | mod bookmark; 10 | mod bookmark_type; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/sidebar/services/bookmarks_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | use jid::BareJid; 9 | 10 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 11 | 12 | use crate::domain::sidebar::models::Bookmark; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait BookmarksService: SendUnlessWasm + SyncUnlessWasm { 18 | async fn load_bookmarks(&self) -> Result>; 19 | async fn save_bookmark(&self, bookmark: &Bookmark) -> Result<()>; 20 | async fn delete_bookmark(&self, jid: &BareJid) -> Result<()>; 21 | } 22 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/sidebar/services/impls/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use sidebar_domain_service::{SidebarDomainService, SidebarDomainServiceDependencies}; 7 | 8 | mod sidebar_domain_service; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/sidebar/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use bookmarks_service::BookmarksService; 7 | pub use sidebar_domain_service::SidebarDomainService; 8 | 9 | mod bookmarks_service; 10 | pub mod impls; 11 | mod sidebar_domain_service; 12 | 13 | #[cfg(feature = "test")] 14 | pub mod mocks { 15 | pub use super::bookmarks_service::MockBookmarksService; 16 | pub use super::sidebar_domain_service::MockSidebarDomainService; 17 | } 18 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/uploads/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod services; 8 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/uploads/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use upload_slot::{UploadHeader, UploadSlot}; 7 | 8 | mod upload_slot; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/uploads/models/upload_slot.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use url::Url; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | pub struct UploadSlot { 10 | pub upload_url: Url, 11 | pub upload_headers: Vec, 12 | pub download_url: Url, 13 | } 14 | 15 | #[derive(Debug, Clone, PartialEq)] 16 | pub struct UploadHeader { 17 | pub name: String, 18 | pub value: String, 19 | } 20 | 21 | impl UploadHeader { 22 | pub fn new(name: impl Into, value: impl Into) -> Self { 23 | Self { 24 | name: name.into(), 25 | value: value.into(), 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/uploads/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use upload_service::UploadService; 7 | 8 | mod upload_service; 9 | 10 | #[cfg(feature = "test")] 11 | pub mod mocks { 12 | pub use super::upload_service::MockUploadService; 13 | } 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/uploads/services/upload_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | use jid::BareJid; 9 | use mime::Mime; 10 | 11 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 12 | 13 | use crate::domain::uploads::models::UploadSlot; 14 | 15 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 16 | #[async_trait] 17 | #[cfg_attr(feature = "test", mockall::automock)] 18 | pub trait UploadService: SendUnlessWasm + SyncUnlessWasm { 19 | async fn request_upload_slot( 20 | &self, 21 | upload_service: &BareJid, 22 | file_name: &str, 23 | file_size: u64, 24 | media_type: &Mime, 25 | ) -> Result; 26 | } 27 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod repos; 8 | pub mod services; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use jabber_client::{JabberClient, PROSE_IM_NODE}; 7 | pub use platform_image::PlatformImage; 8 | pub use presence::Presence; 9 | pub use user_info::{ProfileName, UserInfo, UserInfoOptExt, UserName}; 10 | pub use user_metadata::{LastActivity, UserMetadata}; 11 | pub use user_profile::{Address, Image, UserProfile}; 12 | pub use user_status::UserStatus; 13 | 14 | mod jabber_client; 15 | mod platform_image; 16 | mod presence; 17 | mod user_info; 18 | mod user_metadata; 19 | mod user_profile; 20 | mod user_status; 21 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/models/platform_image.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #[cfg(target_arch = "wasm32")] 7 | pub type PlatformImage = String; 8 | 9 | #[cfg(not(target_arch = "wasm32"))] 10 | pub type PlatformImage = std::path::PathBuf; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/models/presence.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::{Availability, CapabilitiesId}; 7 | use crate::domain::user_info::models::JabberClient; 8 | use crate::dtos::Avatar; 9 | 10 | #[derive(Clone, PartialEq, Debug, Default)] 11 | pub struct Presence { 12 | pub availability: Availability, 13 | pub avatar: Option, 14 | pub caps: Option, 15 | pub client: Option, 16 | pub nickname: Option, 17 | pub priority: i8, 18 | pub status: Option, 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | 25 | #[test] 26 | fn test_default_is_unavailable() { 27 | assert_eq!( 28 | Presence::default(), 29 | Presence { 30 | availability: Availability::Unavailable, 31 | avatar: None, 32 | caps: None, 33 | client: None, 34 | nickname: None, 35 | priority: 0, 36 | status: None, 37 | } 38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/models/user_metadata.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use chrono::{DateTime, FixedOffset, Utc}; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize)] 10 | pub struct UserMetadata { 11 | /// XEP-0202: Entity Time 12 | pub local_time: Option>, 13 | /// XEP-0012: Last Activity 14 | pub last_activity: Option, 15 | } 16 | 17 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 18 | pub struct LastActivity { 19 | /// The time when the user was last active 20 | pub timestamp: DateTime, 21 | /// The status they sent when setting their presence to 'unavailable'. 22 | pub status: Option, 23 | } 24 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/models/user_profile.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use serde::{Deserialize, Serialize}; 7 | use url::Url; 8 | 9 | #[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)] 10 | pub struct Address { 11 | pub locality: Option, 12 | pub country: Option, 13 | } 14 | 15 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 16 | pub enum Image { 17 | Binary { media_type: String, data: Box<[u8]> }, 18 | External(Url), 19 | } 20 | 21 | #[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)] 22 | pub struct UserProfile { 23 | pub first_name: Option, 24 | pub last_name: Option, 25 | pub nickname: Option, 26 | pub org: Option, 27 | pub role: Option, 28 | pub title: Option, 29 | pub email: Option, 30 | pub tel: Option, 31 | pub url: Option, 32 | pub address: Option
, 33 | pub photo: Option, 34 | } 35 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/models/user_status.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 9 | pub struct UserStatus { 10 | pub emoji: String, 11 | pub status: Option, 12 | } 13 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/repos/avatar_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | use prose_xmpp::mods::AvatarData; 11 | 12 | use crate::domain::shared::models::{AccountId, AvatarId, AvatarInfo, EntityIdRef}; 13 | use crate::domain::user_info::models::PlatformImage; 14 | 15 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 16 | #[async_trait] 17 | #[cfg_attr(feature = "test", mockall::automock)] 18 | pub trait AvatarRepository: SendUnlessWasm + SyncUnlessWasm { 19 | /// Returns the cached avatar. 20 | async fn get( 21 | &self, 22 | account: &AccountId, 23 | entity_id: EntityIdRef<'_>, 24 | avatar_id: &AvatarId, 25 | ) -> Result>; 26 | 27 | /// Saves the avatar to the local cache. 28 | async fn set( 29 | &self, 30 | account: &AccountId, 31 | entity_id: EntityIdRef<'_>, 32 | metadata: &AvatarInfo, 33 | image: &AvatarData, 34 | ) -> Result<()>; 35 | 36 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 37 | } 38 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/repos/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use avatar_repository::AvatarRepository; 7 | pub use user_info_repository::{UpdateHandler, UserInfoRepository}; 8 | pub use user_profile_repository::UserProfileRepository; 9 | 10 | mod avatar_repository; 11 | mod user_info_repository; 12 | pub mod user_profile_repository; 13 | 14 | #[cfg(feature = "test")] 15 | pub mod mocks { 16 | pub use super::avatar_repository::MockAvatarRepository; 17 | pub use super::user_info_repository::MockUserInfoRepository; 18 | pub use super::user_profile_repository::MockUserProfileRepository; 19 | } 20 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/repos/user_profile_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::shared::models::{AccountId, ParticipantIdRef}; 12 | use crate::domain::user_info::models::UserProfile; 13 | 14 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 15 | #[async_trait] 16 | #[cfg_attr(feature = "test", mockall::automock)] 17 | pub trait UserProfileRepository: SendUnlessWasm + SyncUnlessWasm { 18 | async fn get( 19 | &self, 20 | account: &AccountId, 21 | participant_id: ParticipantIdRef<'_>, 22 | ) -> Result>; 23 | async fn set( 24 | &self, 25 | account: &AccountId, 26 | participant_id: ParticipantIdRef<'_>, 27 | profile: Option<&UserProfile>, 28 | ) -> Result<()>; 29 | 30 | async fn reset_before_reconnect(&self, account: &AccountId) -> Result<()>; 31 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 32 | } 33 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/services/impls/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use user_info_domain_service::{UserInfoDomainService, UserInfoDomainServiceDependencies}; 7 | 8 | mod user_info_domain_service; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use user_info_domain_service::UserInfoDomainService; 7 | pub use user_info_service::UserInfoService; 8 | 9 | pub mod impls; 10 | mod user_info_domain_service; 11 | mod user_info_service; 12 | 13 | #[cfg(feature = "test")] 14 | pub mod mocks { 15 | pub use super::user_info_domain_service::MockUserInfoDomainService; 16 | pub use super::user_info_service::MockUserInfoService; 17 | } 18 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/user_info/services/user_info_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | use chrono::{DateTime, Utc}; 9 | 10 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 11 | use prose_xmpp::mods::AvatarData; 12 | use prose_xmpp::RequestError; 13 | 14 | use crate::domain::shared::models::{AvatarId, BareEntityId, ParticipantIdRef, UserResourceId}; 15 | use crate::domain::user_info::models::{UserMetadata, UserProfile}; 16 | 17 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 18 | #[async_trait] 19 | #[cfg_attr(feature = "test", mockall::automock)] 20 | pub trait UserInfoService: SendUnlessWasm + SyncUnlessWasm { 21 | async fn load_avatar_image( 22 | &self, 23 | from: &BareEntityId, 24 | image_id: &AvatarId, 25 | ) -> Result, RequestError>; 26 | 27 | async fn load_vcard_temp( 28 | &self, 29 | from: ParticipantIdRef<'_>, 30 | ) -> Result, RequestError>; 31 | 32 | async fn load_user_metadata( 33 | &self, 34 | from: &UserResourceId, 35 | now: DateTime, 36 | ) -> Result, RequestError>; 37 | } 38 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod models; 7 | pub mod repos; 8 | pub mod services; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/models/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use workspace_info::{WorkspaceIcon, WorkspaceInfo}; 7 | 8 | mod workspace_info; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/models/workspace_info.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::{AvatarId, AvatarMetadata, ServerId}; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)] 10 | pub struct WorkspaceInfo { 11 | pub name: Option, 12 | pub icon: Option, 13 | pub accent_color: Option, 14 | } 15 | 16 | #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] 17 | pub struct WorkspaceIcon { 18 | pub id: AvatarId, 19 | pub owner: ServerId, 20 | pub mime_type: String, 21 | } 22 | 23 | impl WorkspaceIcon { 24 | pub fn from_metadata(server_id: ServerId, metadata: AvatarMetadata) -> Self { 25 | Self { 26 | id: metadata.checksum, 27 | owner: server_id, 28 | mime_type: metadata.mime_type, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/repos/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use workspace_info_repository::{UpdateHandler, WorkspaceInfoRepository}; 7 | 8 | mod workspace_info_repository; 9 | 10 | #[cfg(feature = "test")] 11 | pub mod mocks { 12 | pub use super::workspace_info_repository::MockWorkspaceInfoRepository; 13 | } 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/repos/workspace_info_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | 9 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 10 | 11 | use crate::domain::shared::models::AccountId; 12 | use crate::domain::workspace::models::WorkspaceInfo; 13 | 14 | pub type UpdateHandler = Box; 15 | 16 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 17 | #[async_trait] 18 | #[cfg_attr(feature = "test", mockall::automock)] 19 | pub trait WorkspaceInfoRepository: SendUnlessWasm + SyncUnlessWasm { 20 | async fn get(&self, account: &AccountId) -> Result>; 21 | 22 | // Upserts `WorkspaceInfo`. Returns `true` if the `WorkspaceInfo` was changed 23 | // after executing `handler`. 24 | async fn update(&self, account: &AccountId, handler: UpdateHandler) -> Result; 25 | 26 | async fn clear_cache(&self, account: &AccountId) -> Result<()>; 27 | } 28 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/services/impls/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use workspace_info_domain_service::{ 7 | WorkspaceInfoDomainService, WorkspaceInfoDomainServiceDependencies, 8 | }; 9 | 10 | mod workspace_info_domain_service; 11 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/services/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use workspace_info_domain_service::WorkspaceInfoDomainService; 7 | 8 | pub mod impls; 9 | mod workspace_info_domain_service; 10 | 11 | #[cfg(feature = "test")] 12 | pub mod mocks { 13 | pub use super::workspace_info_domain_service::MockWorkspaceInfoDomainService; 14 | } 15 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/domain/workspace/services/workspace_info_domain_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::CachePolicy; 7 | use crate::domain::user_info::models::PlatformImage; 8 | use crate::domain::workspace::models::{WorkspaceIcon, WorkspaceInfo}; 9 | use anyhow::Result; 10 | use async_trait::async_trait; 11 | use prose_wasm_utils::{SendUnlessWasm, SyncUnlessWasm}; 12 | 13 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 14 | #[async_trait] 15 | #[cfg_attr(feature = "test", mockall::automock)] 16 | pub trait WorkspaceInfoDomainService: SendUnlessWasm + SyncUnlessWasm { 17 | async fn get_workspace_info(&self, cache_policy: CachePolicy) -> Result>; 18 | async fn load_workspace_icon(&self, icon: &WorkspaceIcon) -> Result>; 19 | 20 | async fn handle_workspace_info_changed(&self, info: WorkspaceInfo) -> Result<()>; 21 | async fn handle_icon_changed(&self, icon: Option) -> Result<()>; 22 | 23 | async fn reset_before_reconnect(&self) -> Result<()>; 24 | async fn clear_cache(&self) -> Result<()>; 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/account/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | mod user_account_service; 7 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/connection/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | mod connection_service; 7 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/contacts/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use caching_block_list_repository::CachingBlockListRepository; 7 | pub use caching_contacts_repository::CachingContactsRepository; 8 | pub use presence_sub_requests_repository::PresenceSubRequestsRepository; 9 | 10 | mod block_list_service; 11 | mod caching_block_list_repository; 12 | mod caching_contacts_repository; 13 | mod contact_list_service; 14 | mod presence_sub_requests_repository; 15 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/encryption/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use caching_user_device_repository::{CachingUserDeviceRepository, UserDeviceRecord}; 7 | pub use encryption_key_records::{ 8 | KyberPreKeyRecord, LocalDeviceRecord, PreKeyRecord, SenderKeyRecord, SessionRecord, 9 | SignedPreKeyRecord, 10 | }; 11 | pub use encryption_keys_repository::EncryptionKeysRepository; 12 | pub use session_repository::SessionRepository; 13 | 14 | mod caching_user_device_repository; 15 | mod encryption_key_records; 16 | mod encryption_keys_repository; 17 | mod session_repository; 18 | mod user_device_service; 19 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/events/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #[cfg(not(feature = "test"))] 7 | pub use coalescing_client_event_dispatcher::CoalescingClientEventDispatcher; 8 | #[cfg(feature = "test")] 9 | pub use immediate_client_event_dispatcher::ImmediateClientEventDispatcher; 10 | 11 | #[cfg(not(feature = "test"))] 12 | mod coalescing_client_event_dispatcher; 13 | #[cfg(feature = "test")] 14 | mod immediate_client_event_dispatcher; 15 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/general/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use nano_id_provider::NanoIDProvider; 7 | #[cfg(feature = "test")] 8 | pub use rng_provider::mocks; 9 | pub use rng_provider::{OsRngProvider, RngProvider}; 10 | 11 | mod nano_id_provider; 12 | mod request_handling_service; 13 | mod rng_provider; 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/general/nano_id_provider.rs: -------------------------------------------------------------------------------- 1 | use nanoid::nanoid; 2 | use prose_xmpp::IDProvider; 3 | 4 | #[derive(Default)] 5 | pub struct NanoIDProvider {} 6 | 7 | impl IDProvider for NanoIDProvider { 8 | fn new_id(&self) -> String { 9 | let chars = ('a'..='z') 10 | .chain('A'..='Z') 11 | .chain('0'..='9') 12 | .collect::>(); 13 | nanoid!(8, &chars) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/messaging/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use caching_message_repository::CachingMessageRepository; 7 | pub use drafts_repository::{DraftsRecord, DraftsRepository}; 8 | pub use message_record::MessageRecord; 9 | pub use offline_messages_repository::OfflineMessagesRepository; 10 | 11 | mod caching_message_repository; 12 | mod drafts_repository; 13 | mod message_archive_service; 14 | mod message_record; 15 | mod messaging_service; 16 | mod offline_messages_repository; 17 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/messaging/offline_messages_repository.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use parking_lot::Mutex; 7 | 8 | use crate::app::event_handlers::MessageEvent; 9 | use crate::domain::messaging::repos::OfflineMessagesRepository as OfflineMessagesRepositoryTrait; 10 | 11 | pub struct OfflineMessagesRepository { 12 | events: Mutex>, 13 | } 14 | 15 | impl OfflineMessagesRepository { 16 | pub fn new() -> Self { 17 | Self { 18 | events: Default::default(), 19 | } 20 | } 21 | } 22 | 23 | impl OfflineMessagesRepositoryTrait for OfflineMessagesRepository { 24 | fn push(&self, event: MessageEvent) { 25 | self.events.lock().push(event); 26 | } 27 | 28 | fn drain(&self) -> Vec { 29 | self.events.lock().drain(..).collect() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod account; 7 | pub mod connection; 8 | pub mod contacts; 9 | pub mod encryption; 10 | pub mod events; 11 | pub mod general; 12 | pub mod messaging; 13 | pub mod platform_dependencies; 14 | pub mod rooms; 15 | pub mod settings; 16 | pub mod sidebar; 17 | pub mod uploads; 18 | pub mod user_info; 19 | pub mod workspace; 20 | pub mod xmpp; 21 | 22 | pub(crate) mod constants { 23 | #[cfg(not(target_arch = "wasm32"))] 24 | pub(crate) use super::user_info::MAX_IMAGE_DIMENSIONS; 25 | } 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/rooms/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use in_memory_connected_rooms_repository::InMemoryConnectedRoomsRepository; 7 | 8 | mod in_memory_connected_rooms_repository; 9 | mod room_attributes_service; 10 | mod room_management_service; 11 | mod room_participation_service; 12 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/settings/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use account_settings_repository::{AccountSettingsRecord, AccountSettingsRepository}; 7 | pub use local_room_settings_repository::{LocalRoomSettingsRecord, LocalRoomSettingsRepository}; 8 | 9 | mod account_settings_repository; 10 | mod local_room_settings_repository; 11 | mod synced_room_settings_service; 12 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/sidebar/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | mod bookmarks_service; 7 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/uploads/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | mod upload_service; 7 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/uploads/upload_service.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use async_trait::async_trait; 8 | use jid::BareJid; 9 | use mime::Mime; 10 | 11 | use prose_xmpp::mods::HttpUpload; 12 | 13 | use crate::domain::uploads::models::UploadSlot; 14 | use crate::domain::uploads::services::UploadService; 15 | use crate::infra::xmpp::XMPPClient; 16 | 17 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 18 | #[async_trait] 19 | impl UploadService for XMPPClient { 20 | async fn request_upload_slot( 21 | &self, 22 | upload_service: &BareJid, 23 | file_name: &str, 24 | file_size: u64, 25 | media_type: &Mime, 26 | ) -> Result { 27 | let upload_mod = self.client.get_mod::(); 28 | let slot_result = upload_mod 29 | .request_slot( 30 | upload_service, 31 | file_name, 32 | file_size, 33 | Some(media_type.as_ref()), 34 | ) 35 | .await?; 36 | Ok(slot_result.try_into()?) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/user_info/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use in_memory_user_info_repository::InMemoryUserInfoRepository; 7 | pub(self) use presence_map::PresenceMap; 8 | pub use user_profile_repository::{UserProfileRecord, UserProfileRepository}; 9 | 10 | #[cfg(not(target_arch = "wasm32"))] 11 | pub use fs_avatar_repository::*; 12 | #[cfg(target_arch = "wasm32")] 13 | pub use store_avatar_repository::*; 14 | 15 | mod in_memory_user_info_repository; 16 | mod presence_map; 17 | mod user_info_service; 18 | mod user_profile_repository; 19 | 20 | #[cfg(not(target_arch = "wasm32"))] 21 | mod fs_avatar_repository; 22 | #[cfg(target_arch = "wasm32")] 23 | mod store_avatar_repository; 24 | 25 | pub const MAX_IMAGE_DIMENSIONS: (u32, u32) = (400, 400); 26 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/workspace/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use workspace_info_repository::{WorkspaceInfoRecord, WorkspaceInfoRepository}; 7 | 8 | mod workspace_info_repository; 9 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use xmpp_client::{XMPPClient, XMPPClientBuilder}; 7 | 8 | pub mod event_parser; 9 | pub mod type_conversions; 10 | pub mod util; 11 | pub mod xmpp_client; 12 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/avatar_metadata.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use prose_xmpp::stanza::avatar; 7 | 8 | use crate::domain::shared::models::{AvatarId, AvatarMetadata}; 9 | 10 | impl From for AvatarMetadata { 11 | fn from(value: avatar::Info) -> Self { 12 | AvatarMetadata { 13 | bytes: value.bytes as usize, 14 | mime_type: value.r#type, 15 | checksum: AvatarId::from_str_unchecked(value.id.as_ref()), 16 | width: value.width.map(u32::from), 17 | height: value.height.map(u32::from), 18 | url: value.url, 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/compose_state.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::rooms::models::ComposeState; 7 | use xmpp_parsers::chatstates::ChatState; 8 | 9 | impl From for ComposeState { 10 | fn from(value: ChatState) -> Self { 11 | match value { 12 | ChatState::Composing => ComposeState::Composing, 13 | ChatState::Active | ChatState::Gone | ChatState::Inactive | ChatState::Paused => { 14 | ComposeState::Idle 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/contact.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use xmpp_parsers::roster::{Ask, Item, Subscription}; 7 | 8 | use crate::domain::contacts::models::{Contact, PresenceSubscription}; 9 | 10 | impl From for Contact { 11 | fn from(roster_item: Item) -> Self { 12 | let presence_subscription = PresenceSubscription::from(&roster_item); 13 | 14 | Contact { 15 | id: roster_item.jid.into(), 16 | name: roster_item.name, 17 | presence_subscription, 18 | } 19 | } 20 | } 21 | 22 | impl From<&Item> for PresenceSubscription { 23 | fn from(value: &Item) -> Self { 24 | match (&value.subscription, &value.ask) { 25 | (Subscription::None, Ask::Subscribe) => PresenceSubscription::Requested, 26 | (Subscription::None, Ask::None) => PresenceSubscription::None, 27 | (Subscription::Both, _) => PresenceSubscription::Mutual, 28 | (Subscription::From, _) => PresenceSubscription::TheyFollow, 29 | (Subscription::To, _) => PresenceSubscription::WeFollow, 30 | (Subscription::Remove, _) => PresenceSubscription::None, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/mention.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::{bail, Result}; 7 | 8 | use prose_xmpp::stanza::references::{Reference, ReferenceType}; 9 | 10 | use crate::domain::messaging::models::Mention; 11 | use crate::domain::shared::models::UserId; 12 | use crate::dtos::UnicodeScalarIndex; 13 | 14 | impl From for Reference { 15 | fn from(value: Mention) -> Self { 16 | let mut reference = Self::mention(value.user.into_inner()); 17 | reference.begin = value.range.as_ref().map(|r| *r.start.as_ref()); 18 | reference.end = value.range.as_ref().map(|r| *r.end.as_ref()); 19 | reference 20 | } 21 | } 22 | 23 | impl TryFrom for Mention { 24 | type Error = anyhow::Error; 25 | 26 | fn try_from(value: Reference) -> Result { 27 | if value.r#type != ReferenceType::Mention { 28 | bail!("Invalid reference type '{:?}'", value.r#type) 29 | } 30 | 31 | let range = value.begin.and_then(|begin| { 32 | value 33 | .end 34 | .map(|end| UnicodeScalarIndex::new(begin)..UnicodeScalarIndex::new(end)) 35 | }); 36 | 37 | Ok(Self { 38 | user: UserId::from_iri(&value.uri)?, 39 | range, 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub(crate) mod attachment; 7 | pub(crate) mod availability; 8 | mod avatar_metadata; 9 | pub(crate) mod bookmark; 10 | pub(crate) mod caps; 11 | pub(crate) mod compose_state; 12 | pub(crate) mod contact; 13 | pub(crate) mod device_bundle; 14 | pub(crate) mod encrypted_payload; 15 | pub(crate) mod mention; 16 | pub(crate) mod message_ref; 17 | pub(crate) mod room_affiliation; 18 | pub(crate) mod room_info; 19 | pub(crate) mod room_session_participant; 20 | pub(crate) mod room_spec; 21 | pub(crate) mod stanza_error; 22 | pub(crate) mod synced_room_settings; 23 | pub(crate) mod thumbnail; 24 | pub(crate) mod upload_slot; 25 | pub(crate) mod user_activity; 26 | pub(crate) mod user_device; 27 | pub(crate) mod user_profile; 28 | pub(crate) mod workspace_info; 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/room_affiliation.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 5 | 6 | use xmpp_parsers::muc::user::Affiliation; 7 | 8 | use crate::domain::rooms::models::RoomAffiliation; 9 | 10 | impl From for RoomAffiliation { 11 | fn from(value: Affiliation) -> Self { 12 | match value { 13 | Affiliation::Owner => RoomAffiliation::Owner, 14 | Affiliation::Admin => RoomAffiliation::Admin, 15 | Affiliation::Member => RoomAffiliation::Member, 16 | Affiliation::Outcast => RoomAffiliation::Outcast, 17 | Affiliation::None => RoomAffiliation::None, 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/thumbnail.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use mime::Mime; 7 | 8 | pub use prose_xmpp::stanza::media_sharing::Thumbnail as XMPPThumbnail; 9 | 10 | use crate::domain::messaging::models::Thumbnail; 11 | 12 | impl TryFrom for Thumbnail { 13 | type Error = anyhow::Error; 14 | 15 | fn try_from(value: XMPPThumbnail) -> Result { 16 | Ok(Thumbnail { 17 | url: value.uri.parse()?, 18 | media_type: value 19 | .media_type 20 | .map(|mt| mt.parse::()) 21 | .transpose()? 22 | .unwrap_or(mime::APPLICATION_OCTET_STREAM), 23 | width: value.width, 24 | height: value.height, 25 | }) 26 | } 27 | } 28 | 29 | impl From for XMPPThumbnail { 30 | fn from(value: Thumbnail) -> Self { 31 | XMPPThumbnail { 32 | uri: value.url.to_string(), 33 | media_type: Some(value.media_type.to_string()), 34 | width: value.width, 35 | height: value.height, 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/upload_slot.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use xmpp_parsers::http_upload::{Header, SlotResult}; 7 | 8 | use crate::domain::uploads::models::{UploadHeader, UploadSlot}; 9 | 10 | impl TryFrom for UploadSlot { 11 | type Error = anyhow::Error; 12 | 13 | fn try_from(value: SlotResult) -> Result { 14 | Ok(Self { 15 | upload_url: value.put.url.parse()?, 16 | upload_headers: value 17 | .put 18 | .headers 19 | .into_iter() 20 | .map(|h| match h { 21 | Header::Authorization(value) => UploadHeader::new("authorization", value), 22 | Header::Cookie(value) => UploadHeader::new("cookie", value), 23 | Header::Expires(value) => UploadHeader::new("expires", value), 24 | }) 25 | .collect(), 26 | download_url: value.get.url.parse()?, 27 | }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/user_device.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use minidom::Element; 7 | 8 | use prose_xmpp::stanza::omemo::{Device as XMPPDevice, DeviceList as XMPPDeviceList}; 9 | 10 | use crate::domain::encryption::models::{Device, DeviceId, DeviceList}; 11 | 12 | impl TryFrom for DeviceList { 13 | type Error = anyhow::Error; 14 | 15 | fn try_from(value: Element) -> Result { 16 | Ok(Self::from(XMPPDeviceList::try_from(value)?)) 17 | } 18 | } 19 | 20 | impl From for Device { 21 | fn from(value: XMPPDevice) -> Self { 22 | Self { 23 | id: DeviceId::from(value.id), 24 | label: value.label, 25 | } 26 | } 27 | } 28 | 29 | impl From for XMPPDevice { 30 | fn from(value: Device) -> Self { 31 | Self { 32 | id: *value.id.as_ref(), 33 | label: value.label, 34 | } 35 | } 36 | } 37 | 38 | impl From for DeviceList { 39 | fn from(value: XMPPDeviceList) -> Self { 40 | Self { 41 | devices: value.devices.into_iter().map(Into::into).collect(), 42 | } 43 | } 44 | } 45 | 46 | impl From for XMPPDeviceList { 47 | fn from(value: DeviceList) -> Self { 48 | Self { 49 | devices: value.devices.into_iter().map(Into::into).collect(), 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/type_conversions/workspace_info.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2025, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::workspace::models::WorkspaceInfo; 7 | use prose_xmpp::stanza::vcard4::PropertyContainer; 8 | use prose_xmpp::stanza::VCard4; 9 | use prose_xmpp::{ParseError, RequestError}; 10 | 11 | const ACCENT_COLOR_EXTENSION_KEY: &'static str = "x-accent-color"; 12 | 13 | trait PropertyContainerExt { 14 | fn accent_color(&self) -> Option; 15 | } 16 | 17 | impl PropertyContainerExt for PropertyContainer { 18 | fn accent_color(&self) -> Option { 19 | self.get(ACCENT_COLOR_EXTENSION_KEY) 20 | .first() 21 | .map(|v| v.text()) 22 | } 23 | } 24 | 25 | impl TryFrom for WorkspaceInfo { 26 | type Error = RequestError; 27 | 28 | fn try_from(mut vcard: VCard4) -> Result { 29 | if vcard.fn_.is_empty() { 30 | return Err(ParseError::Generic { 31 | msg: "Missing name in Workspace vCard".to_string(), 32 | } 33 | .into()); 34 | } 35 | 36 | Ok(WorkspaceInfo { 37 | name: Some(vcard.fn_.swap_remove(0).value), 38 | icon: None, 39 | accent_color: vcard.unknown_properties.accent_color(), 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/util/caps_ext.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use xmpp_parsers::caps::Caps; 7 | 8 | use crate::domain::shared::models::CapabilitiesId; 9 | use crate::domain::user_info::models::JabberClient; 10 | 11 | pub trait CapsExt { 12 | fn client(&self) -> Option; 13 | fn id(&self) -> CapabilitiesId; 14 | } 15 | 16 | impl CapsExt for Caps { 17 | fn client(&self) -> Option { 18 | self.node.parse().ok() 19 | } 20 | 21 | fn id(&self) -> CapabilitiesId { 22 | CapabilitiesId::new(&self.node, self.hash.to_base64()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/util/file_ext.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use tracing::error; 7 | 8 | use prose_xmpp::stanza::media_sharing::File; 9 | 10 | use crate::domain::messaging::models::Thumbnail; 11 | 12 | pub trait FileExt { 13 | fn best_thumbnail_representation(&self) -> Option; 14 | } 15 | 16 | impl FileExt for File { 17 | fn best_thumbnail_representation(&self) -> Option { 18 | self.thumbnails 19 | .iter() 20 | .find_map(|t| match Thumbnail::try_from(t.clone()) { 21 | Ok(t) => Some(t), 22 | Err(err) => { 23 | error!("Encountered invalid thumbnail in file: {}", err.to_string()); 24 | None 25 | } 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/util/jid_ext.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::str::FromStr; 7 | 8 | use jid::Jid; 9 | 10 | pub trait JidExt { 11 | fn from_iri(iri: &str) -> Result; 12 | } 13 | 14 | #[derive(thiserror::Error, Debug, PartialEq)] 15 | pub enum JidParseError { 16 | #[error("Missing xmpp: prefix in IRI")] 17 | InvalidIRI, 18 | #[error(transparent)] 19 | JID(#[from] jid::Error), 20 | } 21 | 22 | impl JidExt for Jid { 23 | fn from_iri(iri: &str) -> Result { 24 | let Some(mut iri) = iri.strip_prefix("xmpp:") else { 25 | return Err(JidParseError::InvalidIRI); 26 | }; 27 | if let Some(idx) = iri.rfind("?join") { 28 | iri = &iri[..idx]; 29 | } 30 | Ok(Self::from_str(iri)?) 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use prose_xmpp::jid; 37 | 38 | use super::*; 39 | 40 | #[test] 41 | fn test_from_iri() { 42 | assert!(Jid::from_iri("").is_err()); 43 | assert_eq!( 44 | Jid::from_iri("xmpp:room@muc.example.org?join"), 45 | Ok(jid!("room@muc.example.org")) 46 | ); 47 | assert_eq!( 48 | Jid::from_iri("xmpp:room@muc.example.org"), 49 | Ok(jid!("room@muc.example.org")) 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/infra/xmpp/util/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use caps_ext::CapsExt; 7 | pub use file_ext::FileExt; 8 | pub use jid_ext::{JidExt, JidParseError}; 9 | pub use media_share_ext::MediaShareExt; 10 | pub use message_ext::MessageExt; 11 | pub use presence_ext::PresenceExt; 12 | pub use room_occupancy_ext::RoomOccupancyExt; 13 | 14 | mod caps_ext; 15 | mod file_ext; 16 | mod jid_ext; 17 | mod media_share_ext; 18 | mod message_ext; 19 | mod presence_ext; 20 | mod room_occupancy_ext; 21 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/util/jid_workspace.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::domain::shared::models::{BareEntityId, ServerId}; 7 | use jid::{BareJid, FullJid, Jid, NodePart}; 8 | use std::sync::LazyLock; 9 | 10 | static PROSE_WORKSPACE_NODE: LazyLock = 11 | LazyLock::new(|| NodePart::new("prose-workspace").unwrap().into_owned()); 12 | 13 | pub trait ProseWorkspaceJid { 14 | fn is_prose_workspace(&self) -> bool; 15 | } 16 | 17 | impl ProseWorkspaceJid for Jid { 18 | fn is_prose_workspace(&self) -> bool { 19 | self.node() == Some(&*PROSE_WORKSPACE_NODE) 20 | } 21 | } 22 | 23 | impl ProseWorkspaceJid for BareJid { 24 | fn is_prose_workspace(&self) -> bool { 25 | self.node() == Some(&*PROSE_WORKSPACE_NODE) 26 | } 27 | } 28 | 29 | impl ProseWorkspaceJid for FullJid { 30 | fn is_prose_workspace(&self) -> bool { 31 | self.node() == Some(&*PROSE_WORKSPACE_NODE) 32 | } 33 | } 34 | 35 | impl ServerId { 36 | pub(crate) fn to_workspace_entity_id(&self) -> BareEntityId { 37 | BareEntityId::from(BareJid::from_parts( 38 | Some(&*PROSE_WORKSPACE_NODE), 39 | self.as_ref().domain(), 40 | )) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/util/join_all.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::future::Future; 7 | 8 | /// Helper function to run futures in parallel in production or serially in tests 9 | pub async fn join_all(iter: I) -> Vec<::Output> 10 | where 11 | I: IntoIterator, 12 | I::Item: Future, 13 | { 14 | #[cfg(feature = "test")] 15 | { 16 | // Run futures serially in tests 17 | let mut results = Vec::new(); 18 | for future in iter.into_iter() { 19 | results.push(future.await); 20 | } 21 | results 22 | } 23 | #[cfg(not(feature = "test"))] 24 | { 25 | // Run futures in parallel in production 26 | futures::future::join_all(iter).await 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/util/mime_serde_shim.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | // Source: https://github.com/novacrazy/serde_shims/blob/master/mime/src/lib.rs 7 | 8 | use std::fmt; 9 | use std::str::FromStr; 10 | 11 | use mime::Mime; 12 | use serde::{de, Deserializer, Serializer}; 13 | 14 | pub fn serialize(mime: &Mime, serializer: S) -> Result 15 | where 16 | S: Serializer, 17 | { 18 | serializer.serialize_str(mime.as_ref()) 19 | } 20 | 21 | pub fn deserialize<'de, D>(deserializer: D) -> Result 22 | where 23 | D: Deserializer<'de>, 24 | { 25 | struct Visitor; 26 | 27 | impl<'de> de::Visitor<'de> for Visitor { 28 | type Value = Mime; 29 | 30 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 31 | formatter.write_str("a valid MIME type") 32 | } 33 | 34 | fn visit_str(self, value: &str) -> Result 35 | where 36 | E: de::Error, 37 | { 38 | Mime::from_str(value).or_else(|e| Err(E::custom(format!("{}", e)))) 39 | } 40 | } 41 | 42 | deserializer.deserialize_str(Visitor) 43 | } 44 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use coalesce_client_events::coalesce_client_events; 7 | pub use form_config::FormConfig; 8 | pub use join_all::join_all; 9 | pub use path_ext::PathExt; 10 | #[cfg(feature = "debug")] 11 | pub use proxy_transformer::RandomDelayProxyTransformer; 12 | pub use string_ext::StringExt; 13 | 14 | #[cfg(not(target_arch = "wasm32"))] 15 | pub mod account_bookmarks_client; 16 | 17 | mod coalesce_client_events; 18 | pub mod form_config; 19 | pub mod jid_workspace; 20 | mod join_all; 21 | pub mod mime_serde_shim; 22 | mod path_ext; 23 | mod proxy_transformer; 24 | pub mod string_ext; 25 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/util/path_ext.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use mime::Mime; 7 | use std::path::Path; 8 | 9 | pub trait PathExt { 10 | fn media_type(&self) -> Mime; 11 | } 12 | 13 | impl PathExt for Path { 14 | // https://github.com/abonander/mime_guess/issues/88 15 | fn media_type(&self) -> Mime { 16 | let media_type = mime_guess::from_path(self).first_or(mime::APPLICATION_OCTET_STREAM); 17 | 18 | if media_type.type_() == mime::AUDIO && media_type.subtype() == "m4a" { 19 | return "audio/mp4".parse().unwrap(); 20 | } 21 | 22 | media_type 23 | } 24 | } 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use super::*; 29 | 30 | #[test] 31 | fn test_audio_mp4() { 32 | let path = Path::new("audio-file.m4a"); 33 | 34 | let mime_guess_type = mime_guess::from_path(path).first().unwrap(); 35 | assert_eq!(mime_guess_type.type_(), mime::AUDIO); 36 | assert_eq!(mime_guess_type.subtype(), "m4a"); 37 | 38 | let our_type = path.media_type(); 39 | assert_eq!(our_type.type_(), mime::AUDIO); 40 | assert_eq!(our_type.subtype(), "mp4"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/prose-core-client/src/util/proxy_transformer.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::ops::Range; 7 | use std::time::Duration; 8 | 9 | use anyhow::Result; 10 | use async_trait::async_trait; 11 | use minidom::Element; 12 | use prose_wasm_utils::sleep; 13 | use prose_xmpp::connector::{ConnectionEvent, ConnectionEventHandler, ProxyTransformer}; 14 | use prose_xmpp::Connection; 15 | use rand::Rng; 16 | 17 | pub struct RandomDelayProxyTransformer(Range); 18 | 19 | impl RandomDelayProxyTransformer { 20 | pub fn new(range: Range) -> Self { 21 | Self(range) 22 | } 23 | } 24 | 25 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 26 | #[cfg_attr(not(target_arch = "wasm32"), async_trait)] 27 | impl ProxyTransformer for RandomDelayProxyTransformer { 28 | async fn send_stanza(&self, connection: &dyn Connection, stanza: Element) -> Result<()> { 29 | let value = { rand::thread_rng().gen_range(self.0.clone()) }; 30 | sleep(Duration::from_millis(value)).await; 31 | connection.send_stanza(stanza) 32 | } 33 | 34 | async fn receive_stanza( 35 | &self, 36 | connection: Box, 37 | event: ConnectionEvent, 38 | handler: &ConnectionEventHandler, 39 | ) { 40 | (handler)(connection, event).await; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/prose-core-client/tests/event_parsing/main.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | mod connection_event_parser; 7 | mod contact_list_event_parser; 8 | mod message_event_parser; 9 | mod request_event_parser; 10 | mod room_event_parser; 11 | mod sidebar_bookmark_event_parser; 12 | mod user_device_event_parser; 13 | mod user_event_parser; 14 | -------------------------------------------------------------------------------- /crates/prose-core-client/tests/message_parsing/main.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | mod message_parser; 7 | -------------------------------------------------------------------------------- /crates/prose-markup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-markup" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | 7 | [dependencies] 8 | jid = { workspace = true } 9 | pulldown-cmark = "0.11" 10 | pulldown-cmark-escape = "0.11" 11 | 12 | [dev-dependencies] 13 | insta = "1.39" 14 | pretty_assertions = "1.4" -------------------------------------------------------------------------------- /crates/prose-markup/tests/fixtures/complex.md: -------------------------------------------------------------------------------- 1 | # My Complex Markdown Sample 2 | 3 | ## Table of Contents 4 | 5 | 1. [Introduction](#introduction) 6 | 2. [Features](#features) 7 | 3. [Code Example](#code-example) 8 | 4. [Data Table](#data-table) 9 | 5. [Blockquote](#blockquote) 10 | 6. [Conclusion](#conclusion) 11 | 12 | ## Introduction 13 | 14 | Welcome to the **complex Markdown sample**. This document demonstrates various Markdown elements and 15 | how they can be combined to create a rich document. 16 | 17 | ## Features 18 | 19 | - **Headers**: Different levels of headers. 20 | - **Text Formatting**: Bold, italic, and strikethrough. 21 | - **Lists**: Ordered and unordered lists. 22 | - **Tables**: Organize data in a tabular format. 23 | - **Code Blocks**: Display code snippets. 24 | - **Images and Links**: Embed images and hyperlinks. 25 | - **Blockquotes**: Highlight important quotes. 26 | 27 | ### Subsection: Text Formatting 28 | 29 | - *Italic*: Use `*` or `_` around the text. 30 | - **Bold**: Use `**` or `__` around the text. 31 | - ~~Strikethrough~~: Use `~~` around the text. 32 | 33 | ## Code Example 34 | 35 | Here’s a simple Python code snippet: 36 | 37 | ```python 38 | def greet(name): 39 | print(f"Hello, {name}!") 40 | 41 | greet("World") 42 | ``` -------------------------------------------------------------------------------- /crates/prose-markup/tests/fixtures/html_tags.md: -------------------------------------------------------------------------------- 1 | ## HTML Tags 2 | 3 | 4 | 5 | 6 | 7 |
**Hello**, _world_
-------------------------------------------------------------------------------- /crates/prose-markup/tests/fixtures/links.md: -------------------------------------------------------------------------------- 1 | A link with [a title](https://www.example.com) and a plain link: https://www.example.com. 2 | And a link with a [**bold** title](https://www.example.com). 3 | 4 | Hey [@user](xmpp:user@prose.org)! -------------------------------------------------------------------------------- /crates/prose-markup/tests/fixtures/nested_blockquotes.md: -------------------------------------------------------------------------------- 1 | > Level 1.0 2 | > 3 | > > Level 2.0 4 | > > Level 2.1 5 | > > 6 | > > > Level 3.0 7 | > 8 | > Level 1.1 9 | -------------------------------------------------------------------------------- /crates/prose-markup/tests/fixtures/nested_lists.md: -------------------------------------------------------------------------------- 1 | Nested lists example 2 | 3 | 3. Item 1 4 | 4. Item 2 5 | 1. Item 2.1 6 | 2. Item 2.2 7 | 5. Item 3 8 | - Item 3.1 9 | - Item 3.2 10 | 6. Item 4 -------------------------------------------------------------------------------- /crates/prose-markup/tests/fixtures/nested_spans.md: -------------------------------------------------------------------------------- 1 | Some **bold**, _italic_, ~~strikethrough~~ and **_bold italic_** text. -------------------------------------------------------------------------------- /crates/prose-markup/tests/snapshots/styling_writer__complex.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-markup/tests/styling_writer.rs 3 | expression: "markdown_to_message_styling(include_str!(\"fixtures/complex.md\")).unwrap()" 4 | --- 5 | My Complex Markdown Sample 6 | 7 | Table of Contents 8 | 9 | 1. Introduction (#introduction) 10 | 2. Features (#features) 11 | 3. Code Example (#code-example) 12 | 4. Data Table (#data-table) 13 | 5. Blockquote (#blockquote) 14 | 6. Conclusion (#conclusion) 15 | 16 | Introduction 17 | 18 | Welcome to the *complex Markdown sample*. This document demonstrates various Markdown elements and 19 | how they can be combined to create a rich document. 20 | 21 | Features 22 | 23 | * *Headers*: Different levels of headers. 24 | * *Text Formatting*: Bold, italic, and strikethrough. 25 | * *Lists*: Ordered and unordered lists. 26 | * *Tables*: Organize data in a tabular format. 27 | * *Code Blocks*: Display code snippets. 28 | * *Images and Links*: Embed images and hyperlinks. 29 | * *Blockquotes*: Highlight important quotes. 30 | 31 | Subsection: Text Formatting 32 | 33 | * _Italic_: Use `*` or `_` around the text. 34 | * *Bold*: Use `**` or `__` around the text. 35 | * ~Strikethrough~: Use `~~` around the text. 36 | 37 | Code Example 38 | 39 | Here’s a simple Python code snippet: 40 | 41 | ```python 42 | def greet(name): 43 | print(f"Hello, {name}!") 44 | 45 | greet("World") 46 | ``` 47 | -------------------------------------------------------------------------------- /crates/prose-markup/tests/snapshots/styling_writer__escapes_html_tags.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-markup/tests/styling_writer.rs 3 | expression: parser.convert_to_message_styling() 4 | --- 5 | HTML Tags 6 | 7 | 8 | 9 | 10 | 11 |
**Hello**, _world_
12 | -------------------------------------------------------------------------------- /crates/prose-markup/tests/snapshots/styling_writer__links.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-markup/tests/styling_writer.rs 3 | expression: message.body 4 | --- 5 | A link with a title (https://www.example.com) and a plain link: https://www.example.com. 6 | And a link with a *bold* title (https://www.example.com). 7 | 8 | Hey @user! 9 | -------------------------------------------------------------------------------- /crates/prose-markup/tests/snapshots/styling_writer__nested_blockquotes.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-markup/tests/styling_writer.rs 3 | expression: "convert_markdown_to_message_styling(include_str!(\"fixtures/nested_blockquotes.md\"))" 4 | --- 5 | > Level 1.0 6 | > 7 | >> Level 2.0 8 | >> Level 2.1 9 | >> 10 | >>> Level 3.0 11 | >>> 12 | > Level 1.1 13 | > 14 | -------------------------------------------------------------------------------- /crates/prose-markup/tests/snapshots/styling_writer__nested_lists.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-markup/tests/styling_writer.rs 3 | expression: "markdown_to_message_styling(include_str!(\"fixtures/nested_lists.md\")).unwrap()" 4 | --- 5 | Nested lists example 6 | 7 | 3. Item 1 8 | 4. Item 2 9 | 1. Item 2.1 10 | 2. Item 2.2 11 | 5. Item 3 12 | * Item 3.1 13 | * Item 3.2 14 | 6. Item 4 15 | -------------------------------------------------------------------------------- /crates/prose-markup/tests/snapshots/styling_writer__nested_spans.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-markup/tests/styling_writer.rs 3 | expression: "markdown_to_message_styling(include_str!(\"fixtures/nested_spans.md\")).unwrap()" 4 | --- 5 | Some *bold*, _italic_, ~strikethrough~ and *_bold italic_* text. 6 | -------------------------------------------------------------------------------- /crates/prose-proc-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-proc-macros" 3 | version = "0.1.0" 4 | description = "Macros used throughout the project" 5 | license = "MPL-2.0" 6 | edition = "2021" 7 | homepage = "https://github.com/prose-im/prose-core-client" 8 | repository = "https://github.com/prose-im/prose-core-client.git" 9 | keywords = ["xmpp", "xmpp-client", "library"] 10 | categories = ["network-programming"] 11 | authors = ["Marc Bauer "] 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | convert_case = "0.6" 18 | quote = "1.0" 19 | syn = { version = "2.0", features = ["full"] } -------------------------------------------------------------------------------- /crates/prose-store/src/driver/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{Database, StoreError, UpgradeTransaction, VersionChangeEvent}; 2 | use async_trait::async_trait; 3 | use prose_wasm_utils::SendUnlessWasm; 4 | 5 | #[cfg(target_arch = "wasm32")] 6 | pub mod indexed_db; 7 | #[cfg(not(target_arch = "wasm32"))] 8 | pub mod sqlite; 9 | 10 | pub trait ReadMode {} 11 | pub trait WriteMode {} 12 | 13 | pub struct ReadOnly; 14 | pub struct ReadWrite; 15 | 16 | impl ReadMode for ReadOnly {} 17 | impl ReadMode for ReadWrite {} 18 | impl WriteMode for ReadWrite {} 19 | 20 | #[cfg_attr(target_arch = "wasm32", async_trait(? Send))] 21 | #[async_trait] 22 | pub trait Driver: SendUnlessWasm + 'static { 23 | type Error: StoreError + Send + Sync; 24 | type UpgradeTransaction<'db>: UpgradeTransaction<'db, Error = Self::Error>; 25 | type Database: Database; 26 | 27 | async fn open(self, version: u32, update_handler: F) -> Result 28 | where 29 | F: Fn(&VersionChangeEvent>) -> Result<(), Self::Error> 30 | + Send 31 | + 'static; 32 | } 33 | -------------------------------------------------------------------------------- /crates/prose-store/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::{ 2 | define_entity, 3 | driver::Driver, 4 | repository::{Entity, Repository}, 5 | store::Store, 6 | upsert, Database, IndexSpec, IndexedCollection, KeyType, Query, QueryDirection, RawKey, 7 | ReadTransaction, ReadableCollection, StoreError, UpgradeTransaction, WritableCollection, 8 | WriteTransaction, 9 | }; 10 | 11 | #[cfg(target_arch = "wasm32")] 12 | pub use crate::driver::indexed_db::{Error, IndexedDBDriver}; 13 | #[cfg(not(target_arch = "wasm32"))] 14 | pub use crate::driver::sqlite::{Error, SqliteDriver}; 15 | 16 | #[cfg(target_arch = "wasm32")] 17 | pub use IndexedDBDriver as PlatformDriver; 18 | #[cfg(not(target_arch = "wasm32"))] 19 | pub use SqliteDriver as PlatformDriver; 20 | 21 | #[cfg(target_arch = "wasm32")] 22 | pub use crate::driver::indexed_db::Error as DriverError; 23 | #[cfg(not(target_arch = "wasm32"))] 24 | pub use crate::driver::sqlite::Error as DriverError; 25 | -------------------------------------------------------------------------------- /crates/prose-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-utils" 3 | version = "0.1.0" 4 | description = "Shared utils" 5 | license = "MPL-2.0" 6 | edition = "2021" 7 | homepage = "https://github.com/prose-im/prose-core-client" 8 | repository = "https://github.com/prose-im/prose-core-client.git" 9 | keywords = ["xmpp", "xmpp-client", "library"] 10 | categories = ["network-programming"] 11 | authors = ["Marc Bauer "] 12 | -------------------------------------------------------------------------------- /crates/prose-utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod id_string_macro; 2 | -------------------------------------------------------------------------------- /crates/prose-wasm-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-wasm-utils" 3 | version = "0.1.0" 4 | description = "Utils required for Wasm interop" 5 | license = "MPL-2.0" 6 | edition = "2021" 7 | homepage = "https://github.com/prose-im/prose-core-client" 8 | repository = "https://github.com/prose-im/prose-core-client.git" 9 | keywords = ["xmpp", "xmpp-client", "library"] 10 | categories = ["network-programming"] 11 | authors = ["Marc Bauer "] 12 | 13 | [dependencies] 14 | futures = { workspace = true } 15 | tokio = { workspace = true, features = ["macros", "sync"] } 16 | 17 | [target.'cfg(target_arch = "wasm32")'.dependencies] 18 | gloo-timers = { version = "0.3", features = ["futures"] } 19 | wasm-bindgen = { workspace = true } 20 | wasm-bindgen-futures = { workspace = true } 21 | 22 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 23 | tokio = { workspace = true, features = ["rt", "time"] } 24 | tokio-stream = "0.1" -------------------------------------------------------------------------------- /crates/prose-wasm-utils/src/future_ext.rs: -------------------------------------------------------------------------------- 1 | // prose-wasm-utils/prose-wasm-utils 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use futures::FutureExt; 7 | use std::future::Future; 8 | 9 | impl ProseFutureExt for T where T: Future {} 10 | 11 | pub trait ProseFutureExt: Future { 12 | #[cfg(target_arch = "wasm32")] 13 | fn prose_boxed<'a>(self) -> futures::future::LocalBoxFuture<'a, Self::Output> 14 | where 15 | Self: Sized + 'a, 16 | { 17 | self.boxed_local() 18 | } 19 | 20 | #[cfg(not(target_arch = "wasm32"))] 21 | fn prose_boxed<'a>(self) -> futures::future::BoxFuture<'a, Self::Output> 22 | where 23 | Self: Sized + Send + 'a, 24 | { 25 | self.boxed() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/client/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::any::TypeId; 7 | 8 | use parking_lot::RwLock; 9 | 10 | pub use builder::ClientBuilder; 11 | pub use client::Client; 12 | pub(crate) use module_context::ModuleContext; 13 | use prose_wasm_utils::PinnedFuture; 14 | 15 | use crate::connector::ConnectionError; 16 | use crate::connector::Connector; 17 | use crate::mods::AnyModule; 18 | use crate::Event as ClientEvent; 19 | 20 | mod builder; 21 | mod client; 22 | mod module_context; 23 | 24 | #[cfg(target_arch = "wasm32")] 25 | pub type EventHandler = Box PinnedFuture<()>>; 26 | #[cfg(not(target_arch = "wasm32"))] 27 | pub type EventHandler = Box PinnedFuture<()> + Send + Sync>; 28 | 29 | pub(super) type ModuleLookup = Vec<(TypeId, RwLock>)>; 30 | 31 | #[cfg(target_arch = "wasm32")] 32 | pub type ConnectorProvider = Box Box>; 33 | #[cfg(not(target_arch = "wasm32"))] 34 | pub type ConnectorProvider = Box Box + Send + Sync>; 35 | 36 | #[derive(Debug, Clone, PartialEq)] 37 | pub enum Event { 38 | Connected, 39 | Disconnected { error: Option }, 40 | PingTimer, 41 | } 42 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/connector/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use connector::{ 7 | Connection, ConnectionError, ConnectionEvent, ConnectionEventHandler, Connector, 8 | }; 9 | pub use proxy_connector::{ProxyConnector, ProxyTransformer}; 10 | 11 | mod connector; 12 | 13 | mod proxy_connector; 14 | #[cfg(not(target_arch = "wasm32"))] 15 | pub mod xmpp_rs; 16 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/deps/id_provider.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::ops::Deref; 7 | use std::sync::Arc; 8 | use uuid::Uuid; 9 | 10 | pub trait IDProvider: Send + Sync { 11 | fn new_id(&self) -> String; 12 | } 13 | 14 | #[derive(Default)] 15 | pub struct UUIDProvider {} 16 | 17 | impl UUIDProvider { 18 | pub fn new() -> Self { 19 | UUIDProvider {} 20 | } 21 | } 22 | 23 | impl IDProvider for UUIDProvider { 24 | fn new_id(&self) -> String { 25 | Uuid::new_v4().to_string() 26 | } 27 | } 28 | 29 | impl IDProvider for Arc { 30 | fn new_id(&self) -> String { 31 | self.deref().new_id() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/deps/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use id_provider::{IDProvider, UUIDProvider}; 7 | pub use time_provider::{SystemTimeProvider, TimeProvider}; 8 | 9 | mod id_provider; 10 | mod time_provider; 11 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/deps/time_provider.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use chrono::{DateTime, Local, Utc}; 7 | use std::ops::Deref; 8 | use std::sync::Arc; 9 | 10 | pub trait TimeProvider: Send + Sync { 11 | fn now(&self) -> DateTime; 12 | } 13 | 14 | #[derive(Default)] 15 | pub struct SystemTimeProvider {} 16 | 17 | impl TimeProvider for SystemTimeProvider { 18 | fn now(&self) -> DateTime { 19 | Local::now().into() 20 | } 21 | } 22 | 23 | impl TimeProvider for Arc { 24 | fn now(&self) -> DateTime { 25 | self.deref().now() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/event.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::{client, mods}; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | pub enum Event { 10 | BlockList(mods::block_list::Event), 11 | Bookmark(mods::bookmark::Event), 12 | Bookmark2(mods::bookmark2::Event), 13 | Caps(mods::caps::Event), 14 | Chat(mods::chat::Event), 15 | Client(client::Event), 16 | MUC(mods::muc::Event), 17 | Ping(mods::ping::Event), 18 | Profile(mods::profile::Event), 19 | PubSub(mods::pubsub::Event), 20 | Roster(mods::roster::Event), 21 | Status(mods::status::Event), 22 | } 23 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/lib.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use jid::{BareJid, FullJid, Jid}; 7 | pub use secrecy::SecretString; 8 | 9 | pub use client::{Client, ClientBuilder}; 10 | pub use connector::{Connection, ConnectionError, Connector}; 11 | pub use deps::{IDProvider, SystemTimeProvider, TimeProvider, UUIDProvider}; 12 | pub use event::Event; 13 | pub use stanza::ns; 14 | pub use util::{parse_bool, ElementExt, ParseError, PublishOptionsExt, RequestError}; 15 | 16 | pub mod client; 17 | pub mod connector; 18 | mod deps; 19 | mod event; 20 | pub mod mods; 21 | pub mod stanza; 22 | mod util; 23 | 24 | #[cfg(feature = "test")] 25 | pub mod test; 26 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/mam/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub mod query; 7 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/media_sharing/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use file::File; 7 | pub use media_share::MediaShare; 8 | pub use oob::OOB; 9 | pub use thumbnail::Thumbnail; 10 | 11 | mod file; 12 | mod media_share; 13 | mod oob; 14 | mod thumbnail; 15 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/message/content.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use minidom::Element; 7 | use xmpp_parsers::message::MessagePayload; 8 | 9 | use crate::{ns, ElementExt, ParseError}; 10 | 11 | /// XEP-0481: Content Types in Messages 12 | pub struct Content { 13 | pub r#type: String, 14 | pub content: String, 15 | } 16 | 17 | impl From for Element { 18 | fn from(value: Content) -> Self { 19 | Element::builder("content", ns::CONTENT) 20 | .attr("type", value.r#type) 21 | .append(value.content) 22 | .build() 23 | } 24 | } 25 | 26 | impl TryFrom for Content { 27 | type Error = ParseError; 28 | 29 | fn try_from(value: Element) -> Result { 30 | value.expect_is("content", ns::CONTENT)?; 31 | 32 | Ok(Content { 33 | r#type: value.attr_req("type")?.to_string(), 34 | content: value.text(), 35 | }) 36 | } 37 | } 38 | 39 | impl MessagePayload for Content {} 40 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/message/mam.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use minidom::Element; 7 | pub use xmpp_parsers::mam::{Complete, Fin, Query, QueryId}; 8 | use xmpp_parsers::message::MessagePayload; 9 | 10 | use crate::stanza::message::{stanza_id, Forwarded}; 11 | 12 | /// The wrapper around forwarded stanzas. 13 | #[derive(Debug, PartialEq, Clone)] 14 | pub struct ArchivedMessage { 15 | pub id: stanza_id::Id, 16 | pub query_id: Option, 17 | pub forwarded: Forwarded, 18 | } 19 | 20 | impl TryFrom for ArchivedMessage { 21 | type Error = anyhow::Error; 22 | 23 | fn try_from(value: Element) -> Result { 24 | let result = xmpp_parsers::mam::Result_::try_from(value)?; 25 | Ok(ArchivedMessage { 26 | id: result.id.into(), 27 | query_id: result.queryid, 28 | forwarded: result.forwarded.try_into()?, 29 | }) 30 | } 31 | } 32 | 33 | impl From for Element { 34 | fn from(value: ArchivedMessage) -> Self { 35 | xmpp_parsers::mam::Result_ { 36 | id: value.id.into_inner(), 37 | queryid: value.query_id, 38 | forwarded: value.forwarded.into(), 39 | } 40 | .into() 41 | } 42 | } 43 | 44 | impl MessagePayload for ArchivedMessage {} 45 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/message/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use xmpp_parsers::message::MessageType; 7 | 8 | pub use content::Content; 9 | pub use fallback::{Fallback, Range}; 10 | pub use forwarding::Forwarded; 11 | pub use message::{Id, Message}; 12 | pub use muc_user::MucUser; 13 | pub use reactions::{Emoji, Reactions}; 14 | pub use reply::Reply; 15 | 16 | mod builder; 17 | pub mod carbons; 18 | pub mod chat_marker; 19 | mod content; 20 | mod fallback; 21 | pub mod fasten; 22 | mod forwarding; 23 | pub mod mam; 24 | mod message; 25 | mod muc_invite; 26 | mod muc_user; 27 | mod reactions; 28 | mod reply; 29 | pub mod retract; 30 | pub mod stanza_id; 31 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/message/reactions.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use minidom::Element; 7 | use xmpp_parsers::message::MessagePayload; 8 | 9 | use prose_utils::id_string; 10 | 11 | id_string!(Emoji); 12 | 13 | #[derive(Debug, PartialEq, Clone)] 14 | pub struct Reactions { 15 | pub id: String, 16 | pub reactions: Vec, 17 | } 18 | 19 | impl TryFrom for Reactions { 20 | type Error = anyhow::Error; 21 | 22 | fn try_from(value: Element) -> Result { 23 | let reactions = xmpp_parsers::reactions::Reactions::try_from(value)?; 24 | Ok(Reactions { 25 | id: reactions.id, 26 | reactions: reactions 27 | .reactions 28 | .into_iter() 29 | .map(|r| r.emoji.into()) 30 | .collect(), 31 | }) 32 | } 33 | } 34 | 35 | impl From for Element { 36 | fn from(value: Reactions) -> Self { 37 | xmpp_parsers::reactions::Reactions { 38 | id: value.id, 39 | reactions: value 40 | .reactions 41 | .into_iter() 42 | .map(|r| xmpp_parsers::reactions::Reaction { 43 | emoji: r.into_inner(), 44 | }) 45 | .collect(), 46 | } 47 | .into() 48 | } 49 | } 50 | 51 | impl MessagePayload for Reactions {} 52 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/message/retract.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::util::ElementExt; 7 | use minidom::Element; 8 | 9 | use crate::ns; 10 | use crate::stanza::message::fasten; 11 | use crate::stanza::message::fasten::ApplyTo; 12 | 13 | pub struct Retract {} 14 | 15 | impl Default for Retract { 16 | fn default() -> Self { 17 | Retract {} 18 | } 19 | } 20 | 21 | impl From for Element { 22 | fn from(_value: Retract) -> Self { 23 | Element::builder("retract", ns::RETRACT).build() 24 | } 25 | } 26 | 27 | impl TryFrom for Retract { 28 | type Error = anyhow::Error; 29 | 30 | fn try_from(value: Element) -> Result { 31 | value.expect_is("retract", ns::RETRACT)?; 32 | Ok(Retract::default()) 33 | } 34 | } 35 | 36 | impl fasten::ApplyToPayload for Retract {} 37 | 38 | impl ApplyTo { 39 | pub fn retract(&self) -> bool { 40 | self.payloads 41 | .iter() 42 | .find(|p| p.is("retract", ns::RETRACT)) 43 | .is_some() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use xmpp_parsers::presence; 7 | 8 | pub use conference_bookmark::ConferenceBookmark; 9 | pub use last_activity::LastActivityRequest; 10 | pub use message::Message; 11 | pub use pubsub::PubSubMessage; 12 | pub use user_activity::UserActivity; 13 | pub use vcard::VCard; 14 | pub use vcard4::VCard4; 15 | 16 | pub mod avatar; 17 | pub mod conference_bookmark; 18 | pub mod last_activity; 19 | pub mod mam; 20 | pub mod media_sharing; 21 | pub mod message; 22 | pub mod muc; 23 | pub mod ns; 24 | pub mod omemo; 25 | pub mod pubsub; 26 | pub mod references; 27 | pub mod user_activity; 28 | pub mod vcard; 29 | pub mod vcard4; 30 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/muc/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use direct_invite::DirectInvite; 7 | pub use mediated_invite::{Continue, Invite, MediatedInvite}; 8 | pub use muc_user::MucUser; 9 | pub use query::Query; 10 | 11 | pub mod direct_invite; 12 | pub mod mediated_invite; 13 | pub mod muc_user; 14 | pub mod ns; 15 | pub mod query; 16 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/omemo/device.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use minidom::Element; 7 | 8 | use crate::{ns, ElementExt}; 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct Device { 12 | pub id: u32, 13 | pub label: Option, 14 | } 15 | 16 | impl TryFrom for Device { 17 | type Error = anyhow::Error; 18 | 19 | fn try_from(value: Element) -> Result { 20 | value.expect_is("device", ns::LEGACY_OMEMO)?; 21 | 22 | Ok(Self { 23 | id: value.attr_req("id")?.parse::()?.into(), 24 | label: value.attr("label").map(ToString::to_string), 25 | }) 26 | } 27 | } 28 | 29 | impl From for Element { 30 | fn from(value: Device) -> Self { 31 | Element::builder("device", ns::LEGACY_OMEMO) 32 | .attr("id", value.id) 33 | .attr("label", value.label) 34 | .build() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/omemo/device_list.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use minidom::Element; 7 | 8 | use crate::{ns, ElementExt}; 9 | 10 | use super::Device; 11 | 12 | #[derive(Debug, Clone, Default)] 13 | pub struct DeviceList { 14 | pub devices: Vec, 15 | } 16 | 17 | impl TryFrom for DeviceList { 18 | type Error = anyhow::Error; 19 | 20 | fn try_from(value: Element) -> Result { 21 | value.expect_is("list", ns::LEGACY_OMEMO)?; 22 | 23 | Ok(Self { 24 | devices: value 25 | .children() 26 | .map(|child| Device::try_from(child.clone())) 27 | .collect::, _>>()?, 28 | }) 29 | } 30 | } 31 | 32 | impl From for Element { 33 | fn from(value: DeviceList) -> Self { 34 | Element::builder("list", ns::LEGACY_OMEMO) 35 | .append_all(value.devices) 36 | .build() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/omemo/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use device::Device; 7 | pub use device_list::DeviceList; 8 | 9 | mod device; 10 | mod device_list; 11 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/pubsub.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::ns; 7 | use jid::Jid; 8 | use minidom::Element; 9 | use xmpp_parsers::message::Message; 10 | use xmpp_parsers::pubsub::PubSubEvent; 11 | 12 | #[derive(Debug, Clone, PartialEq)] 13 | pub struct PubSubMessage { 14 | pub from: Jid, 15 | pub events: Vec, 16 | } 17 | 18 | impl TryFrom for PubSubMessage { 19 | type Error = anyhow::Error; 20 | 21 | fn try_from(value: Message) -> Result { 22 | let Some(from) = value.from else { 23 | return Err(anyhow::format_err!("Missing from in PubSub message")); 24 | }; 25 | 26 | Ok(PubSubMessage { 27 | from, 28 | events: value 29 | .payloads 30 | .into_iter() 31 | .filter_map(|child| { 32 | if !child.is("event", ns::PUBSUB_EVENT) { 33 | return None; 34 | } 35 | Some(PubSubEvent::try_from(child)) 36 | }) 37 | .collect::>()?, 38 | }) 39 | } 40 | } 41 | 42 | impl From for Element { 43 | fn from(value: PubSubMessage) -> Self { 44 | Element::builder("message", ns::JABBER_CLIENT) 45 | .attr("from", value.from) 46 | .append_all(value.events) 47 | .build() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/stanza/references/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use reference::{Reference, ReferenceType}; 7 | 8 | mod reference; 9 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/test/constant_id_provider.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::IDProvider; 7 | 8 | pub struct ConstantIDProvider { 9 | id: String, 10 | } 11 | 12 | impl ConstantIDProvider { 13 | pub fn new(id: impl Into) -> Self { 14 | Self { id: id.into() } 15 | } 16 | } 17 | 18 | impl IDProvider for ConstantIDProvider { 19 | fn new_id(&self) -> String { 20 | self.id.clone() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/test/incrementing_id_provider.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use std::ops::Deref; 7 | use std::sync::{Arc, Mutex}; 8 | 9 | use crate::IDProvider; 10 | 11 | pub struct IncrementingIDProvider { 12 | prefix: String, 13 | last_id: Mutex, 14 | } 15 | 16 | impl IncrementingIDProvider { 17 | pub fn new(prefix: &str) -> Self { 18 | IncrementingIDProvider { 19 | prefix: prefix.to_string(), 20 | last_id: Mutex::new(0), 21 | } 22 | } 23 | 24 | pub fn reset(&self) { 25 | let mut last_id = self.last_id.lock().unwrap(); 26 | *last_id = 0; 27 | } 28 | 29 | pub fn next_id(&self) -> String { 30 | let last_id = self.last_id.lock().unwrap(); 31 | format!("{}-{}", self.prefix, *last_id + 1) 32 | } 33 | 34 | pub fn last_id(&self) -> String { 35 | let last_id = self.last_id.lock().unwrap(); 36 | format!("{}-{}", self.prefix, *last_id) 37 | } 38 | } 39 | 40 | impl IDProvider for IncrementingIDProvider { 41 | fn new_id(&self) -> String { 42 | let mut last_id = self.last_id.lock().unwrap(); 43 | *last_id += 1; 44 | format!("{}-{}", self.prefix, *last_id) 45 | } 46 | } 47 | 48 | impl IDProvider for Arc { 49 | fn new_id(&self) -> String { 50 | self.deref().new_id() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/util/item_id_ext.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use xmpp_parsers::pubsub::ItemId; 7 | 8 | pub trait ItemIdExt { 9 | fn current() -> Self; 10 | } 11 | 12 | impl ItemIdExt for ItemId { 13 | /// https://xmpp.org/extensions/xep-0060.html#impl-singleton 14 | fn current() -> Self { 15 | ItemId("current".to_string()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | pub use element_ext::{parse_bool, ElementBuilderExt, ElementExt}; 7 | pub use item_id_ext::ItemIdExt; 8 | pub(crate) use module_future_state::{ModuleFuturePoll, ModuleFutureState}; 9 | pub use pub_sub_items_ext::PubSubItemsExt; 10 | pub use pub_sub_query::PubSubQuery; 11 | pub use publish_options_ext::PublishOptionsExt; 12 | pub use request_error::{ParseError, RequestError}; 13 | pub(crate) use request_future::{ElementReducerPoll, RequestFuture}; 14 | pub use xmpp_element::XMPPElement; 15 | 16 | pub mod element_ext; 17 | mod item_id_ext; 18 | mod module_future_state; 19 | mod pub_sub_items_ext; 20 | mod pub_sub_query; 21 | mod publish_options_ext; 22 | mod request_error; 23 | mod request_future; 24 | mod xmpp_element; 25 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/util/module_future_state.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::util::XMPPElement; 7 | use std::task::Waker; 8 | 9 | pub(crate) enum ModuleFuturePoll { 10 | Pending(Option), 11 | Ready(Option), 12 | } 13 | 14 | pub(crate) trait ModuleFutureState: Send { 15 | fn handle_element(&mut self, element: XMPPElement) -> ModuleFuturePoll; 16 | fn fail_with_timeout(&mut self) -> Option; 17 | fn fail_with_disconnect(&mut self) -> Option; 18 | } 19 | -------------------------------------------------------------------------------- /crates/prose-xmpp/src/util/pub_sub_items_ext.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-xmpp 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use minidom::Element; 7 | use xmpp_parsers::pubsub; 8 | 9 | pub trait PubSubItemsExt { 10 | fn find_first_payload>( 11 | self, 12 | name: &str, 13 | ns: &str, 14 | ) -> Result, T::Error>; 15 | } 16 | 17 | impl PubSubItemsExt for Vec { 18 | fn find_first_payload>( 19 | self, 20 | name: &str, 21 | ns: &str, 22 | ) -> Result, T::Error> { 23 | self.into_iter() 24 | .find_map(|item| { 25 | let Some(payload) = &item.payload else { 26 | return None; 27 | }; 28 | if !payload.is(name, ns) { 29 | return None; 30 | } 31 | Some(T::try_from(payload.clone())) 32 | }) 33 | .transpose() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/prose-xmpp/tests/snapshots/bookmark_mod__publishes_bookmark.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-xmpp/tests/bookmark_mod.rs 3 | assertion_line: 114 4 | expression: "sent_stanzas[0]" 5 | --- 6 | User NickRoom passwordhttp://jabber.org/protocol/pubsub#publish-optionstruewhitelist 7 | -------------------------------------------------------------------------------- /crates/prose-xmpp/tests/snapshots/bookmark_mod__publishes_legacy_bookmarks.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: crates/prose-xmpp/tests/bookmark_mod.rs 3 | assertion_line: 193 4 | expression: "sent_stanzas[0]" 5 | --- 6 | User NickRoom passwordhttp://jabber.org/protocol/pubsub#publish-optionstruewhitelist 7 | -------------------------------------------------------------------------------- /examples/.env.example: -------------------------------------------------------------------------------- 1 | ACCOUNT=your_account@prose.org/bot_example 2 | PASSWORD=topsecret -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | cache 2 | logs -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | The examples in this directory require authentication with a XMPP server. To provide your credentials you can either copy the .env.example to .env and insert your credentials or provide them via the command line, e. g. `cargo run --package xmpp-client 'account@your-server.com' 'your-password'`. 2 | 3 | If you're trying to connect to a Prosody instance that uses self-signed certificates enable the feature `insecure-tcp` (e.g. `cargo run -p prose-core-client-cli --features insecure-tcp 'user@localhost' 'pw'`) and set `c2s_require_encryption = false` in your `prosody.cfg.lua`. 4 | 5 | By default logging is done via the tracing-oslog crate. You can see the output on macOS in the Console.app. To only see relevant output set a filter to `subsystem:org.prose`. 6 | -------------------------------------------------------------------------------- /examples/common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | publish = false 7 | 8 | [dependencies] 9 | dotenvy = "0.15" 10 | jid = { workspace = true } 11 | tracing = { workspace = true } 12 | tracing-appender = "0.2" 13 | tracing-oslog = "0.1.2" 14 | tracing-subscriber = { workspace = true, features = ["json"] } -------------------------------------------------------------------------------- /examples/prose-core-client-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-core-client-cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | publish = false 7 | 8 | [dependencies] 9 | anyhow = { workspace = true } 10 | common = { path = "../common" } 11 | dialoguer = "0.10.3" 12 | jid = { workspace = true } 13 | minidom = { workspace = true } 14 | prose-core-client = { path = "../../crates/prose-core-client", features = ["debug", "trace-stanzas"] } 15 | prose-xmpp = { path = "../../crates/prose-xmpp" } 16 | regex = "1.10" 17 | reqwest = { version = "0.11", features = ["stream"] } 18 | strum = { workspace = true } 19 | strum_macros = { workspace = true } 20 | tokio = { workspace = true, features = ["full"] } 21 | url = { workspace = true } 22 | 23 | [features] 24 | insecure-tcp = ["prose-core-client/insecure-tcp"] -------------------------------------------------------------------------------- /examples/xmpp-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xmpp-client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | publish = false 7 | 8 | [dependencies] 9 | anyhow = { workspace = true } 10 | common = { path = "../common" } 11 | futures = { workspace = true } 12 | prose-xmpp = { path = "../../crates/prose-xmpp" } 13 | tokio = { workspace = true, features = ["full"] } 14 | tracing = { workspace = true } 15 | 16 | [features] 17 | insecure-tcp = ["prose-xmpp/insecure-tcp"] -------------------------------------------------------------------------------- /tests/prose-core-integration-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-core-integration-tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | publish = false 7 | 8 | [dependencies] 9 | anyhow = { workspace = true } 10 | async-trait = { workspace = true } 11 | chrono = { workspace = true } 12 | ctor = "0.2" 13 | getrandom = { version = "*", features = ["js"] } 14 | itertools = { workspace = true } 15 | jid = { workspace = true } 16 | minidom = { workspace = true } 17 | parking_lot = { workspace = true } 18 | pretty_assertions = { workspace = true } 19 | prose-core-client = { path = "../../crates/prose-core-client", features = ["test"] } 20 | prose-proc-macros = { path = "../../crates/prose-proc-macros" } 21 | prose-store = { path = "../../crates/prose-store" } 22 | prose-xmpp = { path = "../../crates/prose-xmpp" } 23 | regex = "1.10" 24 | tempfile = { workspace = true } 25 | tracing = { workspace = true } 26 | tracing-subscriber = { workspace = true } 27 | xml-rs = "0.8" 28 | xmpp-parsers = { workspace = true } 29 | 30 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 31 | tokio = { workspace = true, features = ["macros", "rt"] } 32 | 33 | [target.'cfg(target_arch = "wasm32")'.dependencies] 34 | wasm-bindgen-test = "0.3.33" -------------------------------------------------------------------------------- /tests/prose-core-integration-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use tracing::Level; 7 | 8 | #[cfg(not(target_arch = "wasm32"))] 9 | #[ctor::ctor] 10 | fn init() { 11 | let _ = tracing_subscriber::fmt() 12 | .with_test_writer() 13 | // Set this to Level::DEBUG to log SQL queries… 14 | .with_max_level(Level::INFO) 15 | .try_init(); 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests; 20 | 21 | #[cfg(target_arch = "wasm32")] 22 | wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); 23 | -------------------------------------------------------------------------------- /tests/prose-core-integration-tests/src/tests/client/mod.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client/prose-core-integration-tests 2 | // 3 | // Copyright: 2024, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | mod avatar; 7 | mod catchup_unread; 8 | mod contact_list; 9 | mod helpers; 10 | mod message_handling; 11 | mod message_styling; 12 | mod muc; 13 | mod muc_omemo; 14 | mod omemo; 15 | mod reactions; 16 | mod reconnect; 17 | mod reply; 18 | mod user_info; 19 | mod workspace; 20 | -------------------------------------------------------------------------------- /tests/prose-store-integration-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "prose-store-integration-tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | publish = false 7 | 8 | [dependencies] 9 | anyhow = { workspace = true } 10 | async-trait = { workspace = true } 11 | chrono = { workspace = true, features = ["serde"] } 12 | jid = { workspace = true, features = ["serde"] } 13 | pretty_assertions = { workspace = true } 14 | prose-store = { path = "../../crates/prose-store", features = ["test", "chrono", "jid"] } 15 | serde = { workspace = true } 16 | tempfile = { workspace = true } 17 | 18 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 19 | tokio = { workspace = true, features = ["macros", "rt", "sync"] } 20 | insta = { workspace = true } 21 | 22 | [target.'cfg(target_arch = "wasm32")'.dependencies] 23 | wasm-bindgen-test = "0.3.33" -------------------------------------------------------------------------------- /tests/prose-store-integration-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | #[cfg(test)] 7 | mod tests; 8 | 9 | #[cfg(target_arch = "wasm32")] 10 | wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); 11 | -------------------------------------------------------------------------------- /tests/prose-store-integration-tests/src/tests/snapshots/prose_store_integration_tests__tests__sqlite__query_uses_index.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/prose-store-integration-tests/src/tests/sqlite.rs 3 | assertion_line: 109 4 | expression: sql 5 | --- 6 | SEARCH person USING INDEX prose_person_birthday_idx (>? AND =? AND =?) 7 | -------------------------------------------------------------------------------- /tests/prose-store-integration-tests/src/tests/snapshots/prose_store_integration_tests__tests__sqlite__table_structure.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: tests/prose-store-integration-tests/src/tests/sqlite.rs 3 | assertion_line: 90 4 | expression: sql 5 | --- 6 | CREATE TABLE "person" ( 7 | `key` TEXT PRIMARY KEY, 8 | `data` TEXT 9 | ) 10 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1.0" 9 | #cargo-swift = "0.5" 10 | clap = { version = "4.3", features = ["derive"] } 11 | http = "0.2" 12 | jid = { workspace = true } 13 | octocrab = "0.38" 14 | reqwest = { version = "0.11", features = ["stream"] } 15 | semver = "1.0" 16 | serde = { workspace = true } 17 | serde_json = { workspace = true } 18 | tokio = { workspace = true, features = ["full"] } 19 | tokio-util = "0.7" 20 | toml_edit = "0.22" 21 | url = "2.3" 22 | xshell = "0.2" -------------------------------------------------------------------------------- /xtask/src/ci.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use crate::paths; 7 | use anyhow::Result; 8 | use std::path::Path; 9 | use xshell::{cmd, Shell}; 10 | 11 | #[derive(clap::Args)] 12 | pub struct Args { 13 | #[clap(subcommand)] 14 | cmd: Command, 15 | } 16 | 17 | #[derive(clap::Subcommand)] 18 | enum Command { 19 | Wasm, 20 | WasmStore, 21 | } 22 | 23 | impl Args { 24 | pub async fn run(self) -> Result<()> { 25 | let sh = Shell::new()?; 26 | 27 | let path = match self.cmd { 28 | Command::Wasm => paths::tests::INTEGRATION, 29 | Command::WasmStore => paths::tests::STORE, 30 | }; 31 | 32 | sh.change_dir(Path::new(paths::TESTS).join(path)); 33 | run_wasm_integration_tests(&sh) 34 | } 35 | } 36 | 37 | fn run_wasm_integration_tests(sh: &Shell) -> Result<()> { 38 | let args = ["--headless", "--firefox"]; 39 | 40 | cmd!(sh, "wasm-pack test").args(args).run()?; 41 | Ok(()) 42 | } 43 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | // prose-core-client 2 | // 3 | // Copyright: 2023, Marc Bauer 4 | // License: Mozilla Public License v2.0 (MPL v2.0) 5 | 6 | use anyhow::Result; 7 | use clap::{Parser, Subcommand}; 8 | 9 | mod ci; 10 | mod swift; 11 | mod wasm; 12 | 13 | pub(crate) mod paths { 14 | pub const BINDINGS: &str = "bindings"; 15 | pub const TESTS: &str = "tests"; 16 | 17 | pub mod bindings { 18 | pub const WASM: &str = "prose-sdk-js"; 19 | } 20 | pub mod tests { 21 | pub const INTEGRATION: &str = "prose-core-integration-tests"; 22 | pub const STORE: &str = "prose-store-integration-tests"; 23 | } 24 | } 25 | 26 | #[derive(Parser)] 27 | struct Xtask { 28 | #[clap(subcommand)] 29 | cmd: Command, 30 | } 31 | 32 | #[derive(Subcommand)] 33 | enum Command { 34 | WasmPack(wasm::Args), 35 | CI(ci::Args), 36 | Swift(swift::Args), 37 | } 38 | 39 | #[tokio::main] 40 | async fn main() -> Result<()> { 41 | match Xtask::parse().cmd { 42 | Command::WasmPack(args) => args.run().await, 43 | Command::CI(args) => args.run().await, 44 | Command::Swift(args) => args.run().await, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /xtask/src/swift.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | use anyhow::Result; 4 | // use cargo_swift::package::{run, LibTypeArg, Platform}; 5 | // use cargo_swift::{Config, Mode}; 6 | 7 | #[derive(clap::Args)] 8 | pub struct Args { 9 | #[clap(subcommand)] 10 | cmd: Command, 11 | } 12 | 13 | #[derive(clap::Subcommand)] 14 | enum Command { 15 | Build, 16 | } 17 | 18 | impl Args { 19 | pub async fn run(self) -> Result<()> { 20 | todo!("FIXME") 21 | // env::set_current_dir("bindings/prose-sdk-ffi")?; 22 | // 23 | // run( 24 | // Some(vec![Platform::Macos]), 25 | // Some("ProseSDK".to_string()), 26 | // false, 27 | // Config { 28 | // silent: false, 29 | // accept_all: false, 30 | // }, 31 | // Mode::Debug, 32 | // LibTypeArg::Static, 33 | // false, 34 | // )?; 35 | // 36 | // Ok(()) 37 | } 38 | } 39 | --------------------------------------------------------------------------------