Skip to content

Commit

Permalink
Add: Jetpack App as a new upload media extension (#34179)
Browse files Browse the repository at this point in the history
* Add: jetpack app media

* changelog

* update the QR Code url

* Improve empty state

* demo time

* demo time

* Remove the loader

* Add images

* Add JetpackAppIcon

* Add PluginBasePath to the editor initial state

* Update the app-media endpoint to use after attribute

* Updates to the modal to match design

* Fixes

* Order by date.

* Add nice animation when the image gets added.

* Update the URL.

* Only allow the new jetpack media to be added if the Jetpack Experimental blocks is set to true.

* Pull only every 2 seconds instead of every one

* Bring back the close button

* Minor endpoint fixes

* Extract useInterval into its own file.

* Extract useInterval into its own file.

* only display one notice at a time

* Insert first media that is uploaded

* Update the app images

* Add recoleta font

* Update the use the app icon

* More fixes

* Update images

* Remove get-blog-id file

* add useEffect hook when adding only 1 image

* Combine css to be in the same section

* Update the css to use variables and simply the line-height and letter spacing.

* Only load font when experimental block is set.

* More future proof removal of the jetpack app media

* Use experimental variation

* Update to use next-version

Co-authored-by: Jeremy Herve <[email protected]>

* Update to use next-version

Co-authored-by: Jeremy Herve <[email protected]>

* Remoce comment

Co-authored-by: Jeremy Herve <[email protected]>

* Update to use template litterals everywhere for included image

Co-authored-by: Jeremy Herve <[email protected]>

* Add getJetpackBlocksVariation

* Use getJetpackBlocksVariation

* Update comment

* Fix notice overlap

* Fix js notice

* changelog

* Remove alt text since it is image is more decorative

* Add postId to the QR code

* Updates to images to fix missing

* bump version

* Use requestAnimationFrame vs setInterval

---------

Co-authored-by: Jeremy Herve <[email protected]>
  • Loading branch information
enejb and jeherve authored Jan 25, 2024
1 parent 2f27190 commit 6b222cd
Show file tree
Hide file tree
Showing 19 changed files with 645 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Adds new getJetpackBlocksVariation function
2 changes: 1 addition & 1 deletion projects/js-packages/shared-extension-utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ export { default as useAnalytics } from './src/hooks/use-analytics';
export { default as useModuleStatus } from './src/hooks/use-module-status';
export { default as JetpackEditorPanelLogo } from './src/components/jetpack-editor-panel-logo';
export { getBlockIconComponent, getBlockIconProp } from './src/get-block-icon-from-metadata';

export { default as getJetpackBlocksVariation } from './src/get-jetpack-blocks-variation';
export * from './src/modules-state';
2 changes: 1 addition & 1 deletion projects/js-packages/shared-extension-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@automattic/jetpack-shared-extension-utils",
"version": "0.13.10",
"version": "0.14.0-alpha",
"description": "Utility functions used by the block editor extensions",
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/shared-extension-utils/#readme",
"bugs": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import getJetpackData from './get-jetpack-data';
/**
* Returns the jetpack block variation that is defined on the backend.
*
* @returns {?string} options are ['production', 'beta', 'experimental']
*/
export default function getJetpackBlocksVariation() {
const data = getJetpackData();
return data?.blocks_variation ?? 'production';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<?php
/**
* REST API endpoint for the media uploaded by the Jetpack app.
*
* @package automattic/jetpack
* @since $$next-version$$
*/

/**
* Media uploaded by the Jetpack app helper API.
*
* @since $$next-version$$
*/
class WPCOM_REST_API_V2_Endpoint_App_Media extends WP_REST_Controller {

/**
* Constructor.
*/
public function __construct() {
$this->namespace = 'wpcom/v2';
$this->rest_base = 'app-media';

add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}

/**
* Registers the routes for external media.
*/
public function register_routes() {
register_rest_route(
$this->namespace,
$this->rest_base,
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_media' ),
'permission_callback' => array( $this, 'permission_callback' ),
'args' => array(
'number' => array(
'description' => __( 'Number of media items in the request', 'jetpack' ),
'type' => 'number',
'default' => 20,
'required' => false,
'sanitize_callback' => 'absint',

),
'page_handle' => array(
'type' => 'number',
'required' => false,
'sanitize_callback' => 'absint',

),
'after' => array(
'description' => __( 'Timestamp since the media was uploaded', 'jetpack' ),
'type' => 'number',
'default' => 0,
'required' => true,
'sanitize_callback' => 'absint',
),
),
)
);
}

/**
* Checks if a given request has access to external media libraries.
*/
public function permission_callback() {
return current_user_can( 'upload_files' );
}

/**
* Sanitization callback for media parameter.
*
* @param array $param Media parameter.
* @return true|\WP_Error
*/
public function sanitize_media( $param ) {
$param = $this->prepare_media_param( $param );

return rest_sanitize_value_from_schema( $param, $this->media_schema );
}

/**
* Validation callback for media parameter.
*
* @param array $param Media parameter.
* @return true|\WP_Error
*/
public function validate_media( $param ) {
$param = $this->prepare_media_param( $param );
return rest_validate_value_from_schema( $param, $this->media_schema, 'media' );
}

/**
* Decodes guid json and sets parameter defaults.
*
* @param array $param Media parameter.
* @return array
*/
private function prepare_media_param( $param ) {
foreach ( $param as $key => $item ) {
if ( ! empty( $item['guid'] ) ) {
$param[ $key ]['guid'] = json_decode( $item['guid'], true );
}

if ( empty( $param[ $key ]['caption'] ) ) {
$param[ $key ]['caption'] = '';
}
if ( empty( $param[ $key ]['title'] ) ) {
$param[ $key ]['title'] = '';
}
}

return $param;
}

/**
* Retrieves media items from external libraries.
*
* @param \WP_REST_Request $request Full details about the request.
* @return array|\WP_Error|mixed
*/
public function get_media( \WP_REST_Request $request ) {
$params = $request->get_params();
$number = $params['number'];

$query_args = array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'number' => $number,
'date_query' => array(
'after' => gmdate( DATE_RSS, intval( $params['after'] ) ),
),
'paged' => $params['page_handle'],
'author' => get_current_user_id(),
'orderby' => 'date',
);
$media_query = new WP_Query( $query_args );
$response = $this->format_response( $media_query );

wp_reset_postdata();
return $response;
}
/**
* Formats api the response.
*
* @param \WP_Query $media_query Media query.
*/
private function format_response( $media_query ) {
$response = array();
$response['media'] = array();
while ( $media_query->have_posts() ) {
$media_query->the_post();
$response['media'][] = $this->format_item( $media_query->post );
}
$response['found'] = $media_query->found_posts;
$response['meta'] = array( 'next_page' => $media_query->paged + 1 );
return $response;
}
/**
* Formats a single item.
*
* @param \WP_Post $item Media item.
*/
private function format_item( $item ) {
return array(
'ID' => $item->ID,
'url' => get_permalink( $item ),
'date' => get_date_from_gmt( $item->post_date_gmt ),
'name' => get_the_title( $item ),
'file' => basename( wp_get_attachment_image_url( $item->ID, 'full' ) ),
'title' => get_the_title( $item ),
'guid' => get_the_guid( $item ),
'type' => get_post_mime_type( $item ),
'caption' => '',
'thumbnails' => array(
'thumbnail' => wp_get_attachment_image_url( $item->ID, 'thumbnail' ),
'large' => wp_get_attachment_image_url( $item->ID, 'full' ),
),
);
}
}

wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_App_Media' );
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/add-app-upload-media
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

Subject: A new way to upload media via the Jetpack App
5 changes: 5 additions & 0 deletions projects/plugins/jetpack/class.jetpack-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,9 @@ public static function enqueue_block_editor_assets() {
$is_current_user_connected = ( new Connection_Manager( 'jetpack' ) )->is_user_connected();
}

if ( $blocks_variation === 'beta' && $is_current_user_connected ) {
wp_enqueue_style( 'recoleta-font', '//s1.wp.com/i/fonts/recoleta/css/400.min.css', array(), Constants::get_constant( 'JETPACK__VERSION' ) );
}
// AI Assistant
$ai_assistant_state = array(
'is-enabled' => apply_filters( 'jetpack_ai_enabled', true ),
Expand All @@ -720,6 +723,7 @@ public static function enqueue_block_editor_assets() {

$initial_state = array(
'available_blocks' => self::get_availability(),
'blocks_variation' => $blocks_variation,
'modules' => $modules,
'jetpack' => array(
'is_active' => Jetpack::is_connection_ready(),
Expand Down Expand Up @@ -755,6 +759,7 @@ public static function enqueue_block_editor_assets() {
'siteLocale' => str_replace( '_', '-', get_locale() ),
'ai-assistant' => $ai_assistant_state,
'screenBase' => $screen_base,
'pluginBasePath' => plugins_url( '', Constants::get_constant( 'JETPACK__PLUGIN_FILE' ) ),
);

if ( Jetpack::is_module_active( 'publicize' ) && function_exists( 'publicize_init' ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const SOURCE_WORDPRESS = 'wordpress';
export const SOURCE_GOOGLE_PHOTOS = 'google_photos';
export const SOURCE_OPENVERSE = 'openverse';
export const SOURCE_PEXELS = 'pexels';
export const SOURCE_JETPACK_APP_MEDIA = 'jetpack_app_media';

export const PATH_RECENT = 'recent';
export const PATH_ROOT = '/';
export const PATH_OPTIONS = [
Expand Down
131 changes: 131 additions & 0 deletions projects/plugins/jetpack/extensions/shared/external-media/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,137 @@ $grid-size: 8px;
}
}

.jetpack-external-media-browser.is-jetpack-app-media {
max-width: 733px;

.components-modal__content {
@media ( min-width: 600px ) {
width: 90vw;
max-width: 733px;
height: 90vh;
}
}
.jetpack-app-icon {
width: 69px;
}

.components-notice-list {
position: relative;
z-index: 1;
}

.components-modal__header {
border-bottom: 0;
}

.jetpack-external-media-wrapper__jetpack_app_media-instructions {
position: relative;
right: -56px;
bottom: -104px;
}

.components-modal__content {
margin-top: 0;
padding: 40px 56px 40px;
}

.jetpack-external-media-browser__media__item {
opacity: 1;
animation: animate-drop 0.3s ease;
}
@media only screen and ( min-width: 600px ) {
.jetpack-external-media-browser__media__item {
padding-top: 96px;
width: 112px;
}
}

.jetpack-external-media-browser .jetpack-external-media-browser__media__item img {
width: 80px;
height: 80px;
}
}

@keyframes animate-drop {
0% {
opacity: 0;
transform: translateY(-100%);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

.jetpack-external-media-wrapper__jetpack_app_media-title {
font-family: Recoleta, "Noto Serif", Georgia, "Times New Roman", Times, serif;
font-size: 24px;
font-weight: 400;
line-height: 1.67;
letter-spacing: -0.32px;
margin: 0 0 14px 0;
color:var( --jp-gray-100 );

}
.jetpack-external-media-wrapper__jetpack_app_media-description {
font-size: 14px;
font-weight: 400;
line-height: 1.43;
color: var( --jp-gray-60 );
text-align: left;
margin: 0;
}
.jetpack-external-media-wrapper__jetpack_app_media-wrapper.has-no-image-uploaded {
.jetpack-external-media-wrapper__jetpack_app_media-title,
.jetpack-external-media-wrapper__jetpack_app_media-description {

max-width: 100%;
@media only screen and ( min-width: 600px ) {
max-width: calc( 100% - 300px );
}
}
}

.jetpack-external-media-wrapper__jetpack_app_media-qr-code canvas {
width: 100px;
height: 100px;
margin-top: 24px;
}
.jetpack-external-media-wrapper__jetpack_app_media-instructions img {
position: absolute;
right: 56px;
bottom: 0;
display: none;

@media only screen and ( min-width: 600px ) {
display: inline;
}
}
.jetpack-external-media-browser__jetpack_app_media_browser {
margin: 0 -20px ;
.jetpack-external-media-browser__media {
margin: 6px 0 -15px;
}
}

.jetpack-external-media-browser.is-jetpack-app-media .jetpack-external-media-browser__media__toolbar {
padding: 0;
background-color: transparent;
}

.jetpack-external-media-wrapper__jetpack_app_media-qr-code-wrapper {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
flex-grow: 1;
}

.jetpack-external-media-wrapper__jetpack_app_media .jetpack-external-media-browser__empty {
display: none;
}


.jetpack-external-media-browser--is-copying {
pointer-events: none;
}
Expand Down
Loading

0 comments on commit 6b222cd

Please sign in to comment.