diff --git a/config/schema/media_entity_twitter.schema.yml b/config/schema/media_entity_twitter.schema.yml
index 04c37e6..643dfc6 100644
--- a/config/schema/media_entity_twitter.schema.yml
+++ b/config/schema/media_entity_twitter.schema.yml
@@ -6,13 +6,10 @@ media_entity_twitter.settings:
       type: string
       label: 'Base folder for thumbnails'
 
-media_entity.bundle.type.twitter:
-  type: mapping
-  label: 'Twitter type configuration'
+media.source.twitter:
+  type: media.source.field_aware
+  label: '"Twitter" media source configuration'
   mapping:
-    source_field:
-      type: string
-      label: 'Field with embed code/URL'
     use_twitter_api:
       type: boolean
       label: 'Whether to use twitter api or not'
diff --git a/media_entity_twitter.info.yml b/media_entity_twitter.info.yml
index b39b5d7..0e1b79d 100644
--- a/media_entity_twitter.info.yml
+++ b/media_entity_twitter.info.yml
@@ -4,4 +4,4 @@ type: module
 package: Media
 core: 8.x
 dependencies:
-  - media_entity
+  - drupal:media (>= 8.4)
diff --git a/media_entity_twitter.install b/media_entity_twitter.install
index 6e88725..50be0d8 100644
--- a/media_entity_twitter.install
+++ b/media_entity_twitter.install
@@ -10,6 +10,47 @@
  */
 function media_entity_twitter_install() {
   $source = drupal_get_path('module', 'media_entity_twitter') . '/images/icons';
-  $destination = \Drupal::config('media_entity.settings')->get('icon_base');
-  media_entity_copy_icons($source, $destination);
+  $destination = \Drupal::config('media.settings')->get('icon_base_uri');
+  file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+
+  $files = file_scan_directory($source, '/.*\.(svg|png|jpg|jpeg|gif)$/');
+  foreach ($files as $file) {
+    // When reinstalling the media module we don't want to copy the icons when
+    // they already exist. The icons could be replaced (by a contrib module or
+    // manually), so we don't want to replace the existing files. Removing the
+    // files when we uninstall could also be a problem if the files are
+    // referenced somewhere else. Since showing an error that it was not
+    // possible to copy the files is also confusing, we silently do nothing.
+    if (!file_exists($destination . DIRECTORY_SEPARATOR . $file->filename)) {
+      file_unmanaged_copy($file->uri, $destination, FILE_EXISTS_ERROR);
+    }
+  }
+}
+
+/**
+ * Implements hook_requirements().
+ */
+function media_entity_twitter_requirements($phase) {
+  $requirements = [];
+  if ($phase == 'install') {
+    $destination = \Drupal::config('media.settings')->get('icon_base_uri');
+    file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+    $is_writable = is_writable($destination);
+    $is_directory = is_dir($destination);
+    if (!$is_writable || !$is_directory) {
+      if (!$is_directory) {
+        $error = t('The directory %directory does not exist.', ['%directory' => $destination]);
+      }
+      else {
+        $error = t('The directory %directory is not writable.', ['%directory' => $destination]);
+      }
+      $description = t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
+      if (!empty($error)) {
+        $description = $error . ' ' . $description;
+        $requirements['media_entity_twitter']['description'] = $description;
+        $requirements['media_entity_twitter']['severity'] = REQUIREMENT_ERROR;
+      }
+    }
+  }
+  return $requirements;
 }
diff --git a/src/Exception/TwitterApiException.php b/src/Exception/TwitterApiException.php
index 8a44ca8..19db894 100644
--- a/src/Exception/TwitterApiException.php
+++ b/src/Exception/TwitterApiException.php
@@ -15,7 +15,7 @@ class TwitterApiException extends \Exception {
    *   and 'code' elements.
    * @param int $code
    *   (optional) The general error code for the exception.
-   * @param \Exception|NULL $previous
+   * @param \Exception|null $previous
    *   (optional) The previous exception.
    *
    * @see https://dev.twitter.com/overview/api/response-codes
diff --git a/src/Plugin/Field/FieldFormatter/TwitterEmbedFormatter.php b/src/Plugin/Field/FieldFormatter/TwitterEmbedFormatter.php
index 30af408..694452f 100644
--- a/src/Plugin/Field/FieldFormatter/TwitterEmbedFormatter.php
+++ b/src/Plugin/Field/FieldFormatter/TwitterEmbedFormatter.php
@@ -5,7 +5,7 @@ namespace Drupal\media_entity_twitter\Plugin\Field\FieldFormatter;
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FormatterBase;
-use Drupal\media_entity_twitter\Plugin\MediaEntity\Type\Twitter;
+use Drupal\media_entity_twitter\Plugin\media\Source\Twitter;
 
 /**
  * Plugin implementation of the 'twitter_embed' formatter.
@@ -47,7 +47,7 @@ class TwitterEmbedFormatter extends FormatterBase {
    * {@inheritdoc}
    */
   public function viewElements(FieldItemListInterface $items, $langcode) {
-    $element = array();
+    $element = [];
     foreach ($items as $delta => $item) {
       $matches = [];
 
diff --git a/src/Plugin/Validation/Constraint/TweetEmbedCodeConstraintValidator.php b/src/Plugin/Validation/Constraint/TweetEmbedCodeConstraintValidator.php
index 78d19e8..699a819 100644
--- a/src/Plugin/Validation/Constraint/TweetEmbedCodeConstraintValidator.php
+++ b/src/Plugin/Validation/Constraint/TweetEmbedCodeConstraintValidator.php
@@ -2,8 +2,9 @@
 
 namespace Drupal\media_entity_twitter\Plugin\Validation\Constraint;
 
-use Drupal\media_entity\EmbedCodeValueTrait;
-use Drupal\media_entity_twitter\Plugin\MediaEntity\Type\Twitter;
+use Drupal\media_entity_twitter\Plugin\media\Source\Twitter;
+use Drupal\Core\Field\FieldItemList;
+use Drupal\Core\Field\FieldItemInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
 
@@ -12,24 +13,42 @@ use Symfony\Component\Validator\ConstraintValidator;
  */
 class TweetEmbedCodeConstraintValidator extends ConstraintValidator {
 
-  use EmbedCodeValueTrait;
-
   /**
    * {@inheritdoc}
    */
   public function validate($value, Constraint $constraint) {
-    $value = $this->getEmbedCode($value);
-    if (!isset($value)) {
-      return;
+    $data = '';
+    if (is_string($value)) {
+      $data = $value;
     }
-
-    foreach (Twitter::$validationRegexp as $pattern => $key) {
-      if (preg_match($pattern, $value)) {
-        return;
+    elseif ($value instanceof FieldItemList) {
+      $fieldtype = $value->getFieldDefinition()->getType();
+      $field_value = $value->getValue();
+      if ($fieldtype == 'link') {
+        $data = empty($field_value[0]['uri']) ? "" : $field_value[0]['uri'];
+      }
+      else {
+        $data = empty($field_value[0]['value']) ? "" : $field_value[0]['value'];
+      }
+    }
+    elseif ($value instanceof FieldItemInterface) {
+      $class = get_class($value);
+      $property = $class::mainPropertyName();
+      if ($property) {
+        $data = $value->{$property};
+      }
+    }
+    if ($data) {
+      $matches = [];
+      foreach (Twitter::$validationRegexp as $pattern => $key) {
+        if (preg_match($pattern, $data, $item_matches)) {
+          $matches[] = $item_matches;
+        }
+      }
+      if (empty($matches)) {
+        $this->context->addViolation($constraint->message);
       }
     }
-
-    $this->context->addViolation($constraint->message);
   }
 
 }
diff --git a/src/Plugin/Validation/Constraint/TweetVisibleConstraintValidator.php b/src/Plugin/Validation/Constraint/TweetVisibleConstraintValidator.php
index 8843f6f..ccf13fe 100644
--- a/src/Plugin/Validation/Constraint/TweetVisibleConstraintValidator.php
+++ b/src/Plugin/Validation/Constraint/TweetVisibleConstraintValidator.php
@@ -3,8 +3,8 @@
 namespace Drupal\media_entity_twitter\Plugin\Validation\Constraint;
 
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\media_entity\EmbedCodeValueTrait;
-use Drupal\media_entity_twitter\Plugin\MediaEntity\Type\Twitter;
+use Drupal\media_entity_twitter\Plugin\media\Source\Twitter;
+use Drupal\Core\Field\FieldItemInterface;
 use GuzzleHttp\Client;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Validator\Constraint;
@@ -15,8 +15,6 @@ use Symfony\Component\Validator\ConstraintValidator;
  */
 class TweetVisibleConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
 
-  use EmbedCodeValueTrait;
-
   /**
    * The HTTP client to fetch the feed data with.
    *
@@ -45,15 +43,29 @@ class TweetVisibleConstraintValidator extends ConstraintValidator implements Con
    * {@inheritdoc}
    */
   public function validate($value, Constraint $constraint) {
-    $value = $this->getEmbedCode($value);
-    if (!isset($value)) {
-      return;
+    $data = '';
+    if (is_string($value)) {
+      $data = $value;
+    }
+    elseif ($value instanceof FieldItemList) {
+      $fieldtype = $value->getFieldDefinition()->getType();
+      $field_value = $value->getValue();
+      if ($fieldtype == 'link') {
+        $data = empty($field_value[0]['uri']) ? "" : $field_value[0]['uri'];
+      }
+      else {
+        $data = empty($field_value[0]['value']) ? "" : $field_value[0]['value'];
+      }
+    }
+    elseif ($value instanceof FieldItemInterface) {
+      $class = get_class($value);
+      $property = $class::mainPropertyName();
+      if ($property) {
+        $data = $value->{$property};
+      }
     }
-
-    $matches = [];
-
     foreach (Twitter::$validationRegexp as $pattern => $key) {
-      if (preg_match($pattern, $value, $item_matches)) {
+      if (preg_match($pattern, $data, $item_matches)) {
         $matches[] = $item_matches;
       }
     }
diff --git a/src/Plugin/MediaEntity/Type/Twitter.php b/src/Plugin/media/Source/Twitter.php
similarity index 64%
rename from src/Plugin/MediaEntity/Type/Twitter.php
rename to src/Plugin/media/Source/Twitter.php
index ce93d30..597d9bd 100644
--- a/src/Plugin/MediaEntity/Type/Twitter.php
+++ b/src/Plugin/media/Source/Twitter.php
@@ -1,29 +1,33 @@
 <?php
 
-namespace Drupal\media_entity_twitter\Plugin\MediaEntity\Type;
+namespace Drupal\media_entity_twitter\Plugin\media\Source;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Logger\LoggerChannelInterface;
 use Drupal\Core\Render\RendererInterface;
-use Drupal\media_entity\MediaInterface;
-use Drupal\media_entity\MediaTypeBase;
-use Drupal\media_entity\MediaTypeException;
+use Drupal\media\MediaInterface;
+use Drupal\media\MediaSourceBase;
+use Drupal\media\MediaTypeException;
+use Drupal\media\MediaTypeInterface;
 use Drupal\media_entity_twitter\TweetFetcherInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\media\MediaSourceFieldConstraintsInterface;
 
 /**
- * Provides media type plugin for Twitter.
+ * Twitter entity media source.
  *
- * @MediaType(
+ * @MediaSource(
  *   id = "twitter",
  *   label = @Translation("Twitter"),
+ *   allowed_field_types = {"string", "string_long", "link"},
+ *   default_thumbnail_filename = "twitter.png",
  *   description = @Translation("Provides business logic and metadata for Twitter.")
  * )
  */
-class Twitter extends MediaTypeBase {
+class Twitter extends MediaSourceBase implements MediaSourceFieldConstraintsInterface {
 
   /**
    * Config factory service.
@@ -46,13 +50,6 @@ class Twitter extends MediaTypeBase {
    */
   protected $tweetFetcher;
 
-  /**
-   * The logger channel.
-   *
-   * @var \Drupal\Core\Logger\LoggerChannelInterface
-   */
-  protected $logger;
-
   /**
    * {@inheritdoc}
    */
@@ -63,10 +60,10 @@ class Twitter extends MediaTypeBase {
       $plugin_definition,
       $container->get('entity_type.manager'),
       $container->get('entity_field.manager'),
+      $container->get('plugin.manager.field.field_type'),
       $container->get('config.factory'),
       $container->get('renderer'),
-      $container->get('media_entity_twitter.tweet_fetcher'),
-      $container->get('logger.factory')->get('media_entity_twitter')
+      $container->get('media_entity_twitter.tweet_fetcher')
     );
   }
 
@@ -75,9 +72,9 @@ class Twitter extends MediaTypeBase {
    *
    * @var array
    */
-  public static $validationRegexp = array(
+  public static $validationRegexp = [
     '@((http|https):){0,1}//(www\.){0,1}twitter\.com/(?<user>[a-z0-9_-]+)/(status(es){0,1})/(?<id>[\d]+)@i' => 'id',
-  );
+  ];
 
   /**
    * Constructs a new class instance.
@@ -92,21 +89,20 @@ class Twitter extends MediaTypeBase {
    *   Entity type manager service.
    * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
    *   Entity field manager service.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   Config field type manager service.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   Config factory service.
    * @param \Drupal\Core\Render\RendererInterface $renderer
    *   The renderer.
    * @param \Drupal\media_entity_twitter\TweetFetcherInterface $tweet_fetcher
    *   The tweet fetcher.
-   * @param \Drupal\Core\Logger\LoggerChannelInterface $logger
-   *   The logger channel.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory, RendererInterface $renderer, TweetFetcherInterface $tweet_fetcher, LoggerChannelInterface $logger) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $entity_field_manager, $config_factory->get('media_entity.settings'));
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, ConfigFactoryInterface $config_factory, RendererInterface $renderer, TweetFetcherInterface $tweet_fetcher) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $entity_field_manager, $field_type_manager, $config_factory);
     $this->configFactory = $config_factory;
     $this->renderer = $renderer;
     $this->tweetFetcher = $tweet_fetcher;
-    $this->logger = $logger;
   }
 
   /**
@@ -114,38 +110,44 @@ class Twitter extends MediaTypeBase {
    */
   public function defaultConfiguration() {
     return [
+      'source_field' => '',
       'use_twitter_api' => FALSE,
       'generate_thumbnails' => FALSE,
+      'consumer_key' => '',
+      'consumer_secret' => '',
+      'oauth_access_token' => '',
+      'oauth_access_token_secret' => '',
+      'generate_thumbnails' => '',
     ];
   }
 
   /**
    * {@inheritdoc}
    */
-  public function providedFields() {
-    $fields = array(
+  public function getMetadataAttributes() {
+    $attributes = [
       'id' => $this->t('Tweet ID'),
       'user' => $this->t('Twitter user information'),
-    );
+    ];
 
     if ($this->configuration['use_twitter_api']) {
-      $fields += array(
+      $attributes += [
         'image' => $this->t('Link to the twitter image'),
         'image_local' => $this->t('Copies tweet image to the local filesystem and returns the URI.'),
         'image_local_uri' => $this->t('Gets URI of the locally saved image.'),
         'content' => $this->t('This tweet content'),
         'retweet_count' => $this->t('Retweet count for this tweet'),
-        'profile_image_url_https' => $this->t('Link to profile image')
-      );
+        'profile_image_url_https' => $this->t('Link to profile image'),
+      ];
     }
 
-    return $fields;
+    return $attributes;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getField(MediaInterface $media, $name) {
+  public function getMetadata(MediaInterface $media, $attribute_name) {
     $matches = $this->matchRegexp($media);
 
     if (!$matches['id']) {
@@ -153,7 +155,7 @@ class Twitter extends MediaTypeBase {
     }
 
     // First we return the fields that are available from regex.
-    switch ($name) {
+    switch ($attribute_name) {
       case 'id':
         return $matches['id'];
 
@@ -161,12 +163,41 @@ class Twitter extends MediaTypeBase {
         if ($matches['user']) {
           return $matches['user'];
         }
-        return FALSE;
+      case 'thumbnail_uri':
+        // If there's already a local image, use it.
+        if ($local_image = $this->getMetadata($media, 'image_local')) {
+          return $local_image;
+        }
+
+        // If thumbnail generation is disabled, use the default thumbnail.
+        if (empty($this->configuration['generate_thumbnails'])) {
+          return parent::getMetadata($media, $attribute_name);
+        }
+
+        // We might need to generate a thumbnail...
+        $id = $this->getMetadata($media, 'id');
+        $thumbnail_uri = $this->getLocalImageUri($id);
+
+        // ...unless we already have, in which case, use it.
+        if (file_exists($thumbnail_uri)) {
+          return $thumbnail_uri;
+        }
+
+        // Render the thumbnail SVG using the theme system.
+        $thumbnail = [
+          '#theme' => 'media_entity_twitter_tweet_thumbnail',
+          '#content' => $this->getMetadata($media, 'content'),
+          '#author' => $this->getMetadata($media, 'user'),
+          '#avatar' => $this->getMetadata($media, 'profile_image_url_https'),
+        ];
+        $svg = $this->renderer->renderRoot($thumbnail);
+
+        return file_unmanaged_save_data($svg, $thumbnail_uri, FILE_EXISTS_ERROR) ?: parent::getMetadata($media, $attribute_name);
     }
 
     // If we have auth settings return the other fields.
     if ($this->configuration['use_twitter_api'] && $tweet = $this->fetchTweet($matches['id'])) {
-      switch ($name) {
+      switch ($attribute_name) {
         case 'image':
           if (isset($tweet['extended_entities']['media'][0]['media_url'])) {
             return $tweet['extended_entities']['media'][0]['media_url'];
@@ -174,14 +205,14 @@ class Twitter extends MediaTypeBase {
           return FALSE;
 
         case 'image_local':
-          $local_uri = $this->getField($media, 'image_local_uri');
+          $local_uri = $this->getMetadata($media, 'image_local_uri');
 
           if ($local_uri) {
             if (file_exists($local_uri)) {
               return $local_uri;
             }
             else {
-              $image_url = $this->getField($media, 'image');
+              $image_url = $this->getMetadata($media, 'image');
               // @TODO: Use Guzzle, possibly in a service, for this.
               $image_data = file_get_contents($image_url);
               if ($image_data) {
@@ -192,7 +223,7 @@ class Twitter extends MediaTypeBase {
           return FALSE;
 
         case 'image_local_uri':
-          $image_url = $this->getField($media, 'image');
+          $image_url = $this->getMetadata($media, 'image');
           if ($image_url) {
             return $this->getLocalImageUri($matches['id'], $image_url);
           }
@@ -225,79 +256,63 @@ class Twitter extends MediaTypeBase {
    * {@inheritdoc}
    */
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-    $options = [];
-    $allowed_field_types = ['string', 'string_long', 'link'];
-    /** @var \Drupal\media_entity\MediaBundleInterface $bundle */
-    $bundle = $form_state->getFormObject()->getEntity();
-    foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) {
-      if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition()->isBaseField()) {
-        $options[$field_name] = $field->getLabel();
-      }
-    }
+    $form = parent::buildConfigurationForm($form, $form_state);
 
-    $form['source_field'] = array(
-      '#type' => 'select',
-      '#title' => $this->t('Field with source information'),
-      '#description' => $this->t('Field on media entity that stores Twitter embed code or URL. You can create a bundle without selecting a value for this dropdown initially. This dropdown can be populated after adding fields to the bundle.'),
-      '#default_value' => empty($this->configuration['source_field']) ? NULL : $this->configuration['source_field'],
-      '#options' => $options,
-    );
-
-    $form['use_twitter_api'] = array(
+    $form['use_twitter_api'] = [
       '#type' => 'select',
       '#title' => $this->t('Whether to use Twitter api to fetch tweets or not.'),
       '#description' => $this->t("In order to use Twitter's api you have to create a developer account and an application. For more information consult the readme file."),
       '#default_value' => empty($this->configuration['use_twitter_api']) ? 0 : $this->configuration['use_twitter_api'],
-      '#options' => array(
+      '#options' => [
         0 => $this->t('No'),
         1 => $this->t('Yes'),
-      ),
-    );
+      ],
+    ];
 
-    // @todo Evauate if this should be a site-wide configuration.
-    $form['consumer_key'] = array(
+    // @todo: Evaluate if this should be a site-wide configuration.
+    $form['consumer_key'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Consumer key'),
       '#default_value' => empty($this->configuration['consumer_key']) ? NULL : $this->configuration['consumer_key'],
-      '#states' => array(
-        'visible' => array(
-          ':input[name="type_configuration[twitter][use_twitter_api]"]' => array('value' => '1'),
-        ),
-      ),
-    );
+      '#states' => [
+        'visible' => [
+          ':input[name="source_configuration[use_twitter_api]"]' => ['value' => '1'],
+        ],
+      ],
+    ];
 
-    $form['consumer_secret'] = array(
+    $form['consumer_secret'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Consumer secret'),
       '#default_value' => empty($this->configuration['consumer_secret']) ? NULL : $this->configuration['consumer_secret'],
-      '#states' => array(
-        'visible' => array(
-          ':input[name="type_configuration[twitter][use_twitter_api]"]' => array('value' => '1'),
-        ),
-      ),
-    );
+      '#states' => [
+        'visible' => [
+          ':input[name="source_configuration[use_twitter_api]"]' => ['value' => '1'],
+        ],
+      ],
+    ];
 
-    $form['oauth_access_token'] = array(
+    $form['oauth_access_token'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Oauth access token'),
       '#default_value' => empty($this->configuration['oauth_access_token']) ? NULL : $this->configuration['oauth_access_token'],
-      '#states' => array(
-        'visible' => array(
-          ':input[name="type_configuration[twitter][use_twitter_api]"]' => array('value' => '1'),
-        ),
-      ),
-    );
+      '#states' => [
+        'visible' => [
+          ':input[name="source_configuration[use_twitter_api]"]' => ['value' => '1'],
+        ],
+      ],
+    ];
 
-    $form['oauth_access_token_secret'] = array(
+    $form['oauth_access_token_secret'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Oauth access token secret'),
       '#default_value' => empty($this->configuration['oauth_access_token_secret']) ? NULL : $this->configuration['oauth_access_token_secret'],
-      '#states' => array(
-        'visible' => array(
-          ':input[name="type_configuration[twitter][use_twitter_api]"]' => array('value' => '1'),
-        ),
-      ),
-    );
+      '#states' => [
+        'visible' => [
+          ':input[name="source_configuration[use_twitter_api]"]' => ['value' => '1'],
+        ],
+      ],
+    ];
 
     $form['generate_thumbnails'] = [
       '#type' => 'checkbox',
@@ -305,7 +320,7 @@ class Twitter extends MediaTypeBase {
       '#default_value' => $this->configuration['generate_thumbnails'],
       '#states' => [
         'visible' => [
-          ':input[name="type_configuration[twitter][use_twitter_api]"]' => [
+          ':input[name="source_configuration[use_twitter_api]"]' => [
             'checked' => TRUE,
           ],
         ],
@@ -321,20 +336,18 @@ class Twitter extends MediaTypeBase {
   /**
    * {@inheritdoc}
    */
-  public function attachConstraints(MediaInterface $media) {
-    parent::attachConstraints($media);
+  public function getSourceFieldConstraints() {
+    return [
+      'TweetEmbedCode' => [],
+      'TweetVisible' => [],
+    ];
+  }
 
-    if (isset($this->configuration['source_field'])) {
-      $source_field_name = $this->configuration['source_field'];
-      if ($media->hasField($source_field_name)) {
-        foreach ($media->get($source_field_name) as &$embed_code) {
-          /** @var \Drupal\Core\TypedData\DataDefinitionInterface $typed_data */
-          $typed_data = $embed_code->getDataDefinition();
-          $typed_data->addConstraint('TweetEmbedCode');
-          $typed_data->addConstraint('TweetVisible');
-        }
-      }
-    }
+  /**
+   * {@inheritdoc}
+   */
+  public function createSourceField(MediaTypeInterface $type) {
+    return parent::createSourceField($type)->set('label', 'Tweet Url');
   }
 
   /**
@@ -377,52 +390,10 @@ class Twitter extends MediaTypeBase {
     return $local_uri;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getDefaultThumbnail() {
-    return $this->config->get('icon_base') . '/twitter.png';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function thumbnail(MediaInterface $media) {
-    // If there's already a local image, use it.
-    if ($local_image = $this->getField($media, 'image_local')) {
-      return $local_image;
-    }
-
-    // If thumbnail generation is disabled, use the default thumbnail.
-    if (empty($this->configuration['generate_thumbnails'])) {
-      return $this->getDefaultThumbnail();
-    }
-
-    // We might need to generate a thumbnail...
-    $id = $this->getField($media, 'id');
-    $thumbnail_uri = $this->getLocalImageUri($id);
-
-    // ...unless we already have, in which case, use it.
-    if (file_exists($thumbnail_uri)) {
-      return $thumbnail_uri;
-    }
-
-    // Render the thumbnail SVG using the theme system.
-    $thumbnail = [
-      '#theme' => 'media_entity_twitter_tweet_thumbnail',
-      '#content' => $this->getField($media, 'content'),
-      '#author' => $this->getField($media, 'user'),
-      '#avatar' => $this->getField($media, 'profile_image_url_https'),
-    ];
-    $svg = $this->renderer->renderRoot($thumbnail);
-
-    return file_unmanaged_save_data($svg, $thumbnail_uri, FILE_EXISTS_ERROR) ?: $this->getDefaultThumbnail();
-  }
-
   /**
    * Runs preg_match on embed code/URL.
    *
-   * @param MediaInterface $media
+   * @param \Drupal\media\MediaInterface $media
    *   Media object.
    *
    * @return array|bool
@@ -431,14 +402,14 @@ class Twitter extends MediaTypeBase {
    * @see preg_match()
    */
   protected function matchRegexp(MediaInterface $media) {
-    $matches = array();
+    $matches = [];
 
     if (isset($this->configuration['source_field'])) {
       $source_field = $this->configuration['source_field'];
       if ($media->hasField($source_field)) {
-        $property_name = $media->{$source_field}->first()->mainPropertyName();
+        $property_name = $media->get($source_field)->first()->mainPropertyName();
         foreach (static::$validationRegexp as $pattern => $key) {
-          if (preg_match($pattern, $media->{$source_field}->{$property_name}, $matches)) {
+          if (preg_match($pattern, $media->get($source_field)->{$property_name}, $matches)) {
             return $matches;
           }
         }
diff --git a/src/Tests/TweetEmbedFormatterTest.php b/src/Tests/TweetEmbedFormatterTest.php
deleted file mode 100644
index 1625f49..0000000
--- a/src/Tests/TweetEmbedFormatterTest.php
+++ /dev/null
@@ -1,208 +0,0 @@
-<?php
-
-namespace Drupal\media_entity_twitter\Tests;
-
-use Drupal\simpletest\WebTestBase;
-use Drupal\media_entity\Tests\MediaTestTrait;
-
-/**
- * Tests for Twitter embed formatter.
- *
- * @group media_entity_twitter
- */
-class TweetEmbedFormatterTest extends WebTestBase {
-
-  use MediaTestTrait;
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array(
-    'media_entity_twitter',
-    'media_entity',
-    'node',
-    'field_ui',
-    'views_ui',
-    'block',
-    'link',
-  );
-
-  /**
-   * The test user.
-   *
-   * @var \Drupal\User\UserInterface
-   */
-  protected $adminUser;
-
-  /**
-   * Media entity machine id.
-   *
-   * @var string
-   */
-  protected $mediaId = 'twitter';
-
-  /**
-   * The test media bundle.
-   *
-   * @var \Drupal\media_entity\MediaBundleInterface
-   */
-  protected $testBundle;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp() {
-    parent::setUp();
-
-    $bundle['bundle'] = $this->mediaId;
-    $this->testBundle = $this->drupalCreateMediaBundle($bundle, 'twitter');
-    $this->drupalPlaceBlock('local_actions_block');
-    $this->adminUser = $this->drupalCreateUser([
-      'administer media',
-      'administer media bundles',
-      'administer media fields',
-      'administer media form display',
-      'administer media display',
-      // Media entity permissions.
-      'view media',
-      'create media',
-      'update media',
-      'update any media',
-      'delete media',
-      'delete any media',
-      // Other permissions.
-      'administer views',
-    ]);
-    $this->drupalLogin($this->adminUser);
-  }
-
-  /**
-   * Tests adding and editing a twitter embed formatter.
-   */
-  public function testManageEmbedFormatter() {
-    // Test and create one media bundle.
-    $bundle = $this->testBundle;
-
-    // Assert that the media bundle has the expected values before proceeding.
-    $this->drupalGet('admin/structure/media/manage/' . $bundle->id());
-    $this->assertFieldByName('label', $bundle->label());
-    $this->assertFieldByName('type', 'twitter');
-
-    // Add and save link field type settings (Embed code).
-    $this->drupalGet('admin/structure/media/manage/' . $bundle->id() . '/fields/add-field');
-    $edit_conf = [
-      'new_storage_type' => 'link',
-      'label' => 'Link URL',
-      'field_name' => 'link_url',
-    ];
-    $this->drupalPostForm(NULL, $edit_conf, t('Save and continue'));
-    $this->assertText('These settings apply to the ' . $edit_conf['label'] . ' field everywhere it is used.');
-    $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => '1',
-    ];
-    $this->drupalPostForm(NULL, $edit, t('Save field settings'));
-    $this->assertText('Updated field ' . $edit_conf['label'] . ' field settings.');
-
-    // Set the new link field type as required.
-    $edit = [
-      'required' => TRUE,
-      'settings[link_type]' => '16',
-      'settings[title]' => '0',
-    ];
-    $this->drupalPostForm(NULL, $edit, t('Save settings'));
-    $this->assertText('Saved ' . $edit_conf['label'] . ' configuration.');
-
-    // Add and save string_long field type settings (Embed code).
-    $this->drupalGet('admin/structure/media/manage/' . $bundle->id() . '/fields/add-field');
-    $edit_conf = [
-      'new_storage_type' => 'string_long',
-      'label' => 'Embed code',
-      'field_name' => 'embed_code',
-    ];
-    $this->drupalPostForm(NULL, $edit_conf, t('Save and continue'));
-    $this->assertText('These settings apply to the ' . $edit_conf['label'] . ' field everywhere it is used.');
-    $edit = [
-      'cardinality' => 'number',
-      'cardinality_number' => '1',
-    ];
-    $this->drupalPostForm(NULL, $edit, t('Save field settings'));
-    $this->assertText('Updated field ' . $edit_conf['label'] . ' field settings.');
-
-    // Set the new string_long field type as required.
-    $edit = [
-      'required' => TRUE,
-    ];
-    $this->drupalPostForm(NULL, $edit, t('Save settings'));
-    $this->assertText('Saved ' . $edit_conf['label'] . ' configuration.');
-
-    // Assert that the new field types configurations have been successfully
-    // saved.
-    $xpath = $this->xpath('//*[@id="field-link-url"]');
-    $this->assertEqual((string) $xpath[0]->td[0], 'Link URL');
-    $this->assertEqual((string) $xpath[0]->td[1], 'field_link_url');
-    $this->assertEqual((string) $xpath[0]->td[2]->a, 'Link');
-
-    $xpath = $this->xpath('//*[@id="field-embed-code"]');
-    $this->assertEqual((string) $xpath[0]->td[0], 'Embed code');
-    $this->assertEqual((string) $xpath[0]->td[1], 'field_embed_code');
-    $this->assertEqual((string) $xpath[0]->td[2]->a, 'Text (plain, long)');
-
-    // Test if edit worked and if new fields values have been saved as
-    // expected.
-    $this->drupalGet('admin/structure/media/manage/' . $bundle->id());
-    $this->assertFieldByName('label', $bundle->label());
-    $this->assertFieldByName('type', 'twitter');
-    $this->assertFieldByName('type_configuration[twitter][source_field]', 'field_embed_code');
-    $this->drupalPostForm(NULL, NULL, t('Save media bundle'));
-    $this->assertText('The media bundle ' . $bundle->label() . ' has been updated.');
-    $this->assertText($bundle->label());
-
-    $this->drupalGet('admin/structure/media/manage/' . $bundle->id() . '/display');
-
-    // Set and save the settings of the new field types.
-    $edit = [
-      'fields[field_link_url][label]' => 'above',
-      'fields[field_link_url][type]' => 'twitter_embed',
-      'fields[field_embed_code][label]' => 'above',
-      'fields[field_embed_code][type]' => 'twitter_embed',
-    ];
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    $this->assertText('Your settings have been saved.');
-
-    // Create and save the media with a twitter media code.
-    $this->drupalGet('media/add/' . $bundle->id());
-
-    // Random image url from twitter.
-    $tweet_url = 'https://twitter.com/RamzyStinson/status/670650348319576064';
-
-    // Random image from twitter.
-    $tweet = '<blockquote class="twitter-tweet" lang="it"><p lang="en" dir="ltr">' .
-             'Midnight project. I ain&#39;t got no oven. So I improvise making this milo crunchy kek batik. hahahaha ' .
-             '<a href="https://twitter.com/hashtag/itssomething?src=hash">#itssomething</a> ' .
-             '<a href="https://t.co/Nvn4Q1v2ae">pic.twitter.com/Nvn4Q1v2ae</a></p>&mdash; Zi (@RamzyStinson) ' .
-             '<a href="https://twitter.com/RamzyStinson/status/670650348319576064">' .
-             '28 Novembre 2015</a></blockquote><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>';
-
-    $edit = [
-      'name[0][value]' => 'Title',
-      'field_link_url[0][uri]' => $tweet_url,
-      'field_embed_code[0][value]' => $tweet,
-    ];
-    $this->drupalPostForm(NULL, $edit, t('Save and publish'));
-
-    // Assert that the media has been successfully saved.
-    $this->assertText('Title');
-
-    // Assert that the link url formatter exists on this page.
-    $this->assertText('Link URL');
-    $this->assertRaw('<a href="https://twitter.com/RamzyStinson/statuses/670650348319576064">', 'Link in embedded Tweet found.');
-
-    // Assert that the string_long code formatter exists on this page.
-    $this->assertText('Embed code');
-    $this->assertRaw('<blockquote class="twitter-tweet', 'Embedded Tweet found.');
-  }
-
-}
diff --git a/src/TweetFetcher.php b/src/TweetFetcher.php
index 1c4b211..88b442b 100644
--- a/src/TweetFetcher.php
+++ b/src/TweetFetcher.php
@@ -35,7 +35,7 @@ class TweetFetcher implements TweetFetcherInterface {
   /**
    * TweetFetcher constructor.
    *
-   * @param \Drupal\Core\Cache\CacheBackendInterface|NULL $cache
+   * @param \Drupal\Core\Cache\CacheBackendInterface|null $cache
    *   (optional) A cache bin for storing fetched tweets.
    */
   public function __construct(CacheBackendInterface $cache = NULL) {
diff --git a/src/TweetFetcherInterface.php b/src/TweetFetcherInterface.php
index a4dbf33..037cd4b 100644
--- a/src/TweetFetcherInterface.php
+++ b/src/TweetFetcherInterface.php
@@ -12,7 +12,7 @@ interface TweetFetcherInterface {
    *
    * @param int $id
    *   The tweet ID.
-
+   *
    * @return array
    *   The tweet information.
    *
@@ -35,7 +35,7 @@ interface TweetFetcherInterface {
    *
    * @param string $consumer_key
    *   The consumer key.
-   * @param $consumer_secret
+   * @param string $consumer_secret
    *   The consumer secret.
    * @param string $oauth_access_token
    *   The OAuth access token.
diff --git a/tests/src/Functional/TweetEmbedFormatterTest.php b/tests/src/Functional/TweetEmbedFormatterTest.php
new file mode 100644
index 0000000..bfdeca2
--- /dev/null
+++ b/tests/src/Functional/TweetEmbedFormatterTest.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace Drupal\Tests\media_entity_twitter\Functional;
+
+use Drupal\Tests\media\Functional\MediaFunctionalTestBase;
+
+/**
+ * Tests for Twitter embed formatter.
+ *
+ * @group media_entity_twitter
+ */
+class TweetEmbedFormatterTest extends MediaFunctionalTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'media_entity_twitter',
+    'link',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Tests adding and editing a twitter embed formatter.
+   */
+  public function testManageEmbedFormatter() {
+    // Test and create one media type.
+    $bundle = $this->createMediaType(['bundle' => 'twitter'], 'twitter');
+
+    // We need to fix widget and formatter config for the default field.
+    $source = $bundle->getSource();
+    $source_field = $source->getSourceFieldDefinition($bundle);
+    // Use the default widget and settings.
+    $component = \Drupal::service('plugin.manager.field.widget')
+      ->prepareConfiguration('string', []);
+
+    // @todo Replace entity_get_form_display() when #2367933 is done.
+    // https://www.drupal.org/node/2872159.
+    entity_get_form_display('media', $bundle->id(), 'default')
+      ->setComponent($source_field->getName(), $component)
+      ->save();
+
+    // Assert that the media type has the expected values before proceeding.
+    $this->drupalGet('admin/structure/media/manage/' . $bundle->id());
+    $this->assertFieldByName('label', $bundle->label());
+    $this->assertFieldByName('source', 'twitter');
+
+    // Add and save string_long field type settings (Embed code).
+    $this->drupalGet('admin/structure/media/manage/' . $bundle->id() . '/fields/add-field');
+    $edit_conf = [
+      'new_storage_type' => 'string_long',
+      'label' => 'Embed code',
+      'field_name' => 'embed_code',
+    ];
+    $this->drupalPostForm(NULL, $edit_conf, t('Save and continue'));
+    $this->assertText('These settings apply to the ' . $edit_conf['label'] . ' field everywhere it is used.');
+    $edit = [
+      'cardinality' => 'number',
+      'cardinality_number' => '1',
+    ];
+    $this->drupalPostForm(NULL, $edit, t('Save field settings'));
+    $this->assertText('Updated field ' . $edit_conf['label'] . ' field settings.');
+
+    // Set the new string_long field type as required.
+    $edit = [
+      'required' => TRUE,
+    ];
+    $this->drupalPostForm(NULL, $edit, t('Save settings'));
+    $this->assertText('Saved ' . $edit_conf['label'] . ' configuration.');
+
+    // Assert that the new field types configurations have been successfully
+    // saved.
+    $this->drupalGet('admin/structure/media/manage/' . $bundle->id() . '/fields');
+    $xpath = $this->xpath('//*[@id=:id]/td', [':id' => 'field-media-twitter']);
+    $this->assertEqual((string) $xpath[0]->getText(), 'Tweet Url');
+    $this->assertEqual((string) $xpath[1]->getText(), 'field_media_twitter');
+    $this->assertEqual((string) $xpath[2]->find('css', 'a')->getText(), 'Text (plain)');
+
+    $xpath = $this->xpath('//*[@id=:id]/td', [':id' => 'field-embed-code']);
+    $this->assertEqual((string) $xpath[0]->getText(), 'Embed code');
+    $this->assertEqual((string) $xpath[1]->getText(), 'field_embed_code');
+    $this->assertEqual((string) $xpath[2]->find('css', 'a')->getText(), 'Text (plain, long)');
+
+    $this->drupalGet('admin/structure/media/manage/' . $bundle->id() . '/display');
+
+    // Set and save the settings of the new field types.
+    $edit = [
+      'fields[field_media_twitter][parent]' => 'content',
+      'fields[field_media_twitter][region]' => 'content',
+      'fields[field_media_twitter][label]' => 'above',
+      'fields[field_media_twitter][type]' => 'twitter_embed',
+      'fields[field_embed_code][label]' => 'above',
+      'fields[field_embed_code][type]' => 'twitter_embed',
+    ];
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertText('Your settings have been saved.');
+
+    // Create and save the media with a twitter media code.
+    $this->drupalGet('media/add/' . $bundle->id());
+
+    // Random image url from twitter.
+    $tweet_url = 'https://twitter.com/RamzyStinson/status/670650348319576064';
+
+    // Random image from twitter.
+    $tweet = '<blockquote class="twitter-tweet" lang="it"><p lang="en" dir="ltr">' .
+             'Midnight project. I ain&#39;t got no oven. So I improvise making this milo crunchy kek batik. hahahaha ' .
+             '<a href="https://twitter.com/hashtag/itssomething?src=hash">#itssomething</a> ' .
+             '<a href="https://t.co/Nvn4Q1v2ae">pic.twitter.com/Nvn4Q1v2ae</a></p>&mdash; Zi (@RamzyStinson) ' .
+             '<a href="https://twitter.com/RamzyStinson/status/670650348319576064">' .
+             '28 Novembre 2015</a></blockquote><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>';
+
+    $edit = [
+      'name[0][value]' => 'Title',
+      'field_media_twitter[0][value]' => $tweet_url,
+      'field_embed_code[0][value]' => $tweet,
+    ];
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+
+    // Assert that the media has been successfully saved.
+    $this->assertText('Title');
+
+    // Assert that the link url formatter exists on this page.
+    $this->assertText('Tweet Url');
+    $this->assertRaw('<a href="https://twitter.com/RamzyStinson/statuses/670650348319576064">', 'Link in embedded Tweet found.');
+
+    // Assert that the string_long code formatter exists on this page.
+    $this->assertText('Embed code');
+    $this->assertRaw('<blockquote class="twitter-tweet', 'Embedded Tweet found.');
+  }
+
+}
diff --git a/tests/src/Kernel/ThumbnailTest.php b/tests/src/Kernel/ThumbnailTest.php
index 850b78a..a39cda2 100644
--- a/tests/src/Kernel/ThumbnailTest.php
+++ b/tests/src/Kernel/ThumbnailTest.php
@@ -5,10 +5,9 @@ namespace Drupal\Tests\media_entity_twitter\Kernel;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\KernelTests\KernelTestBase;
-use Drupal\media_entity\Entity\Media;
-use Drupal\media_entity\Entity\MediaBundle;
-use Drupal\media_entity\MediaInterface;
-use Drupal\media_entity_twitter\Plugin\MediaEntity\Type\Twitter;
+use Drupal\media\Entity\Media;
+use Drupal\media\Entity\MediaType;
+use Drupal\media_entity_twitter\Plugin\media\Source\Twitter;
 use Drupal\media_entity_twitter\TweetFetcherInterface;
 
 /**
@@ -21,21 +20,21 @@ class ThumbnailTest extends KernelTestBase {
   /**
    * The mocked tweet fetcher.
    *
-   * @var TweetFetcherInterface
+   * @var \Drupal\media_entity_twitter\TweetFetcherInterface
    */
   protected $tweetFetcher;
 
   /**
    * The plugin under test.
    *
-   * @var Twitter
+   * @var \Drupal\media_entity_twitter\Plugin\media\Source\Twitter
    */
   protected $plugin;
 
   /**
    * A tweet media entity.
    *
-   * @var MediaInterface
+   * @var \Drupal\media\MediaInterface
    */
   protected $entity;
 
@@ -46,7 +45,7 @@ class ThumbnailTest extends KernelTestBase {
     'field',
     'file',
     'image',
-    'media_entity',
+    'media',
     'media_entity_twitter',
     'system',
     'text',
@@ -65,10 +64,10 @@ class ThumbnailTest extends KernelTestBase {
     $this->tweetFetcher = $this->getMock(TweetFetcherInterface::class);
     $this->container->set('media_entity_twitter.tweet_fetcher', $this->tweetFetcher);
 
-    MediaBundle::create([
+    MediaType::create([
       'id' => 'tweet',
-      'type' => 'twitter',
-      'type_configuration' => [
+      'source' => 'twitter',
+      'source_configuration' => [
         'source_field' => 'tweet',
         'use_twitter_api' => TRUE,
         'consumer_key' => $this->randomString(),
@@ -97,9 +96,9 @@ class ThumbnailTest extends KernelTestBase {
 
     $this->plugin = Twitter::create(
       $this->container,
-      MediaBundle::load('tweet')->getTypeConfiguration(),
+      MediaType::load('tweet')->get('source_configuration'),
       'twitter',
-      []
+      MediaType::load('tweet')->getSource()->getPluginDefinition()
     );
 
     $dir = $this->container
@@ -128,7 +127,7 @@ class ThumbnailTest extends KernelTestBase {
 
     $uri = 'public://twitter-thumbnails/12345.ico';
     touch($uri);
-    $this->assertEquals($uri, $this->plugin->thumbnail($this->entity));
+    $this->assertEquals($uri, $this->plugin->getMetadata($this->entity, 'thumbnail_uri'));
   }
 
   /**
@@ -147,7 +146,7 @@ class ThumbnailTest extends KernelTestBase {
         ],
       ]);
 
-    $this->plugin->thumbnail($this->entity);
+    $this->plugin->getMetadata($this->entity, 'thumbnail_uri');
     $this->assertFileExists('public://twitter-thumbnails/12345.ico');
   }
 
@@ -156,8 +155,8 @@ class ThumbnailTest extends KernelTestBase {
    */
   public function testNoLocalImage() {
     $this->assertEquals(
-      $this->plugin->getDefaultThumbnail(),
-      $this->plugin->thumbnail($this->entity)
+      '/twitter.png',
+      $this->plugin->getMetadata($this->entity, 'thumbnail_uri')
     );
   }
 
@@ -169,7 +168,7 @@ class ThumbnailTest extends KernelTestBase {
     $configuration['generate_thumbnails'] = TRUE;
     $this->plugin->setConfiguration($configuration);
 
-    $uri = $this->plugin->thumbnail($this->entity);
+    $uri = $this->plugin->getMetadata($this->entity, 'thumbnail_uri');
     $this->assertFileExists($uri);
   }
 
