def asSingleStoryPanel()

in common/app/common/TrailsToShowcase.scala [127:243]


  def asSingleStoryPanel(content: PressedContent): Either[Seq[String], SingleStoryPanel] = {
    val proposedTitle = titleOfLengthFrom(MaxSinglePanelTitleLength, content)
    val proposedPanelTitle = panelTitleFrom(content)
    val proposedWebUrl = webUrl(content).map(Right(_)).getOrElse(Left(Seq("Trail had no web url")))
    val proposedImageUrl = singleStoryImageUrlFor(content)

    // Decide which type of panel to produce
    // If supporting content is present try to map into into a related articles article group for a Related Articles panel
    // If then try to produce a bullet item list for a Bullets panel
    // This might get more interesting if we implement Key Moments but the principal is the same.
    val supportingContent = content match {
      case curatedContent: CuratedContent =>
        curatedContent.supportingContent
      case _ => Seq.empty
    }

    val isRelatedArticlePanel = supportingContent.nonEmpty
    val proposedArticleGroup = {
      if (isRelatedArticlePanel) {
        if (supportingContent.nonEmpty) {
          makeArticlesFrom(
            supportingContent,
            2,
            "Related Article",
            MaxRelatedArticleTitleLength,
            MaxRelatedArticleOverlineLength,
          ).fold(
            { l =>
              Left(l)
            },
            { articles: Seq[Article] =>
              Right(Some(ArticleGroup(role = "RELATED_CONTENT", articles = articles)))
            },
          )
        } else {
          Right(None)
        }
      } else {
        // Not a related article panel so we will gracefully opt out
        Right(None)
      }
    }

    val proposedBulletList = {
      if (!isRelatedArticlePanel) {
        content.card.trailText
          .map(extractBulletsFrom)
          .getOrElse(Left(Seq("No trail text available to create a bullet list from")))
          .fold(
            { l => Left(l) },
            { bulletList =>
              Right(Some(bulletList))
            },
          )
      } else {
        Right(None)
      }
    }

    val proposedOverline = overlineFrom(content, MaxOverlineLength)

    val proposedSummary = {
      if (isRelatedArticlePanel) {
        extractRelatedArticlePanelSummaryFrom(content)
      } else {
        Right(None)
      }
    }

    val proposedPublicationDate =
      content.card.webPublicationDateOption.toRight(Seq("Could not find web publication date for panel"))

    // Collect all mandatory values; any missing will result in a None entry
    val maybePanel = for {
      title <- proposedTitle.toOption
      maybePanelTitle <- proposedPanelTitle.toOption
      webUrl <- proposedWebUrl.toOption
      imageUrl <- proposedImageUrl.toOption
      maybeOverline <- proposedOverline.toOption
      bulletList <- proposedBulletList.toOption
      articleGroup <- proposedArticleGroup.toOption
      summary <- proposedSummary.toOption
      published <- proposedPublicationDate.toOption
    } yield {
      // Build a panel
      SingleStoryPanel(
        title = title,
        panelTitle = maybePanelTitle,
        link = webUrl,
        author = bylineFrom(content),
        overline = maybeOverline,
        imageUrl = imageUrl,
        published = published,
        updated = content.card.lastModifiedOption.getOrElse(published),
        articleGroup = articleGroup,
        bulletList = bulletList,
        summary = summary,
      )
    }

    maybePanel.map { Right(_) }.getOrElse {
      // Round up all of the potential sources of hard errors and collect their objections
      Left(
        Seq(
          proposedTitle,
          proposedPanelTitle,
          proposedWebUrl,
          proposedImageUrl,
          proposedOverline,
          proposedArticleGroup,
          proposedBulletList,
          proposedSummary,
          proposedPublicationDate,
        ).flatMap(_.left.toOption).flatten,
      )
    }
  }