Packages
phoenix_kit
1.7.51
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/publishing/web/editor/persistence.ex
defmodule PhoenixKit.Modules.Publishing.Web.Editor.Persistence do
@moduledoc """
Post persistence operations for the publishing editor.
Handles create, update, and save operations for posts,
including version creation and translation saving.
"""
use Gettext, backend: PhoenixKitWeb.Gettext
alias PhoenixKit.Modules.Publishing
alias PhoenixKit.Modules.Publishing.DBStorage
alias PhoenixKit.Modules.Publishing.ListingCache
alias PhoenixKit.Modules.Publishing.PubSub, as: PublishingPubSub
alias PhoenixKit.Modules.Publishing.Renderer
alias PhoenixKit.Modules.Publishing.Storage
alias PhoenixKit.Modules.Publishing.Web.Editor.Forms
alias PhoenixKit.Modules.Publishing.Web.Editor.Helpers
require Logger
# ============================================================================
# Save Orchestration
# ============================================================================
@doc """
Performs save operation with validation and routing.
Returns {:noreply, socket}.
"""
def perform_save(socket) do
params =
socket.assigns.form
|> Map.take(["status", "published_at", "slug", "featured_image_id", "url_slug"])
|> Map.put("content", socket.assigns.content)
params =
case {socket.assigns.group_mode, Map.get(params, "slug")} do
{"slug", slug} when is_binary(slug) and slug != "" ->
params
{"slug", _} ->
Map.delete(params, "slug")
_ ->
Map.delete(params, "slug")
end
# Validate url_slug before saving (for translations)
# For directory slug conflicts, we auto-clear and show a notice instead of blocking
case validate_url_slug_for_save(socket, params) do
{:ok, validated_params} ->
do_perform_save(socket, validated_params)
{:ok, validated_params, notice} ->
socket = Phoenix.LiveView.put_flash(socket, :info, notice)
do_perform_save(socket, validated_params)
{:error, reason} ->
error_message = url_slug_error_message(reason)
{:noreply, Phoenix.LiveView.put_flash(socket, :error, error_message)}
end
end
defp validate_url_slug_for_save(socket, params) do
url_slug = Map.get(params, "url_slug", "")
if url_slug != "" do
group_slug = socket.assigns.group_slug
language = editor_language(socket.assigns)
post_slug = socket.assigns.post.slug
case Storage.validate_url_slug(group_slug, url_slug, language, post_slug) do
{:ok, _} ->
{:ok, params}
{:error, :conflicts_with_directory_slug} ->
# Auto-clear the url_slug from ALL translations of this post
cleared_params = Map.put(params, "url_slug", "")
cleared_languages = DBStorage.clear_url_slug_from_post(group_slug, post_slug, url_slug)
notice =
if length(cleared_languages) > 1 do
gettext(
"Custom URL slug '%{slug}' was cleared from %{count} translations because it conflicts with another post's directory name",
slug: url_slug,
count: length(cleared_languages)
)
else
gettext(
"Custom URL slug '%{slug}' for %{language} was cleared because it conflicts with another post's directory name",
slug: url_slug,
language: language
)
end
{:ok, cleared_params, notice}
{:error, :slug_already_exists} ->
# Auto-clear the url_slug from ALL translations of this post
cleared_params = Map.put(params, "url_slug", "")
cleared_languages = DBStorage.clear_url_slug_from_post(group_slug, post_slug, url_slug)
notice =
if length(cleared_languages) > 1 do
gettext(
"Custom URL slug '%{slug}' was cleared from %{count} translations because it's already in use by another post",
slug: url_slug,
count: length(cleared_languages)
)
else
gettext(
"Custom URL slug '%{slug}' for %{language} was cleared because it's already in use by another post",
slug: url_slug,
language: language
)
end
{:ok, cleared_params, notice}
{:error, reason} ->
{:error, reason}
end
else
{:ok, params}
end
end
defp url_slug_error_message(:invalid_format),
do: gettext("URL slug must be lowercase letters, numbers, and hyphens only")
defp url_slug_error_message(:reserved_language_code),
do: gettext("URL slug cannot be a language code")
defp url_slug_error_message(:reserved_route_word),
do: gettext("URL slug cannot be a reserved word (admin, api, assets, etc.)")
defp do_perform_save(socket, params) do
is_new_post = Map.get(socket.assigns, :is_new_post, false)
is_new_translation = Map.get(socket.assigns, :is_new_translation, false)
# Check if translation file was created in background
{socket, is_new_translation} =
if is_new_translation do
check_background_translation_creation(socket)
else
{socket, false}
end
cond do
is_new_post ->
create_new_post(socket, params)
is_new_translation ->
create_new_translation(socket, params)
true ->
update_existing_post(socket, params)
end
end
defp check_background_translation_creation(socket) do
target_language = socket.assigns.current_language
# Check if content was created in DB for this language.
# Verify the returned post's language matches — resolve_content falls back
# to the primary language when the requested language doesn't exist yet,
# which would trick us into thinking the translation was already created.
case re_read_post(socket, target_language, socket.assigns.post[:version]) do
{:ok, real_post}
when real_post.language == target_language and
real_post.content != nil and real_post.content != "" ->
socket =
socket
|> Phoenix.Component.assign(:post, real_post)
|> Phoenix.Component.assign(:is_new_translation, false)
{socket, false}
_ ->
{socket, true}
end
end
# ============================================================================
# Create Operations
# ============================================================================
defp create_new_post(socket, params) do
scope = socket.assigns[:phoenix_kit_current_scope]
create_opts =
if socket.assigns.group_mode == "slug" do
%{
title: Map.get(params, "title"),
slug: Map.get(params, "slug")
}
else
%{}
end
|> Map.put(:scope, scope)
case Publishing.create_post(socket.assigns.group_slug, create_opts) do
{:ok, new_post} ->
uuid = new_post[:uuid]
result =
case Publishing.update_post(socket.assigns.group_slug, new_post, params, %{scope: scope}) do
{:ok, updated_post} ->
# Preserve UUID from create_post (update_post returns FS post without it)
{:ok, if(uuid, do: Map.put(updated_post, :uuid, uuid), else: updated_post)}
error ->
error
end
handle_post_update_result(socket, result, gettext("Post created and saved"), %{
is_new_post: false
})
{:error, error} ->
handle_post_creation_error(socket, error, gettext("Failed to create post"))
end
end
defp create_new_translation(socket, params) do
scope = socket.assigns[:phoenix_kit_current_scope]
original_identifier = socket.assigns.post.slug
current_version = socket.assigns[:current_version]
case Publishing.add_language_to_post(
socket.assigns.group_slug,
original_identifier,
socket.assigns.current_language,
current_version
) do
{:ok, new_post} ->
case Publishing.update_post(socket.assigns.group_slug, new_post, params, %{
scope: scope,
is_primary_language: false
}) do
{:ok, _updated_post} = result ->
handle_post_update_result(
socket,
result,
gettext("Translation created and saved"),
%{
is_new_translation: false,
original_post_path: nil
}
)
error ->
handle_post_update_result(
socket,
error,
gettext("Translation created and saved"),
%{
is_new_translation: false,
original_post_path: nil
}
)
end
{:error, _reason} ->
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext("Failed to create translation file")
)}
end
end
# ============================================================================
# Update Operations
# ============================================================================
defp update_existing_post(socket, params) do
scope = socket.assigns[:phoenix_kit_current_scope]
post = socket.assigns.post
language = socket.assigns.current_language
old_path = post.path
# Check if we need to create a new version
should_create_version =
not Map.get(post, :is_legacy_structure, false) and
Storage.should_create_new_version?(post, params, language)
if should_create_version do
create_new_version_from_edit(socket, params, scope)
else
update_post_in_place(socket, params, scope, old_path)
end
end
defp create_new_version_from_edit(socket, params, scope) do
group_slug = socket.assigns.group_slug
post = socket.assigns.post
case Publishing.create_new_version(group_slug, post, params, %{scope: scope}) do
{:ok, new_version_post} ->
invalidate_post_cache(group_slug, new_version_post)
socket =
socket
|> Phoenix.Component.assign(:post, new_version_post)
|> Phoenix.Component.assign(:content, new_version_post.content)
|> Phoenix.Component.assign(:current_version, new_version_post.version)
|> Phoenix.Component.assign(:available_versions, new_version_post.available_versions)
|> Phoenix.Component.assign(:version_statuses, new_version_post.version_statuses)
|> Phoenix.Component.assign(
:version_dates,
Map.get(new_version_post, :version_dates, %{})
)
|> Phoenix.Component.assign(:editing_published_version, false)
|> Phoenix.Component.assign(:has_pending_changes, false)
|> Phoenix.LiveView.push_event("changes-status", %{has_changes: false})
|> Phoenix.LiveView.put_flash(
:info,
gettext("Created new version %{version} (draft)",
version: new_version_post.version
)
)
|> Phoenix.LiveView.push_patch(
to:
Helpers.build_edit_url(group_slug, new_version_post,
version: new_version_post.version
)
)
{:noreply, socket}
{:error, reason} ->
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext("Failed to create new version: %{reason}", reason: inspect(reason))
)}
end
end
defp update_post_in_place(socket, params, scope, old_path) do
group_slug = socket.assigns.group_slug
# Ensure the post's language matches the current editing language,
# not the stale language from when the post was initially loaded
post = %{socket.assigns.post | language: socket.assigns.current_language}
current_version = socket.assigns[:current_version]
# Use saved_status (on-disk status) not post.metadata.status (form-updated status)
saved_status = socket.assigns[:saved_status] || Map.get(post.metadata, :status, "draft")
is_primary_language = socket.assigns[:is_primary_language] == true
# For translations: if primary is not published, force status to match primary
params = enforce_translation_status(params, socket, is_primary_language)
new_status = Map.get(params, "status")
# Check if this is a status change TO published for a versioned post
is_publishing =
should_publish_version?(
is_primary_language,
new_status,
saved_status,
current_version,
post
)
case Publishing.update_post(group_slug, post, params, %{
scope: scope,
is_primary_language: is_primary_language
}) do
{:ok, updated_post} ->
handle_successful_update(
socket,
updated_post,
old_path,
is_publishing,
post,
current_version
)
{:error, error} ->
handle_post_in_place_error(socket, error)
end
end
defp enforce_translation_status(params, _socket, true = _is_primary), do: params
defp enforce_translation_status(params, socket, false = _is_primary) do
primary_status = socket.assigns.form["status"]
if primary_status == "published" do
params
else
Map.put(params, "status", primary_status)
end
end
defp should_publish_version?(is_primary, new_status, current_status, current_version, post) do
is_primary and
new_status == "published" and
current_status != "published" and
current_version != nil and
not Map.get(post, :is_legacy_structure, false)
end
# ============================================================================
# Success/Error Handlers
# ============================================================================
defp handle_successful_update(
socket,
updated_post,
old_path,
false = _is_publishing,
_post,
_version
) do
handle_post_save_success(socket, updated_post, old_path)
end
defp handle_successful_update(
socket,
updated_post,
old_path,
true = _is_publishing,
post,
current_version
) do
group_slug = socket.assigns.group_slug
post_identifier = post.slug
# Use user ID so all tabs for the same user recognize their own publishes
user_id =
get_in(socket.assigns, [:phoenix_kit_current_scope, Access.key(:user), Access.key(:id)])
case Publishing.publish_version(group_slug, post_identifier, current_version,
source_id: user_id
) do
:ok ->
handle_post_save_success(socket, updated_post, old_path)
{:error, reason} ->
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:warning,
gettext("Post saved but failed to archive other versions: %{reason}",
reason: inspect(reason)
)
)}
end
end
defp handle_post_save_success(socket, post, old_path) do
group_slug = socket.assigns.group_slug
invalidate_post_cache(group_slug, post)
# Broadcast save to other tabs/users
if socket.assigns[:form_key] do
Logger.debug(
"BROADCASTING editor_saved from update_existing_post: " <>
"form_key=#{inspect(socket.assigns.form_key)}, source=#{inspect(socket.id)}"
)
PublishingPubSub.broadcast_editor_saved(socket.assigns.form_key, socket.id)
end
flash_message =
if socket.assigns.is_autosaving,
do: nil,
else: gettext("Post saved")
# Re-read post to get fresh cross-version statuses
current_version = socket.assigns[:current_version]
current_language = socket.assigns[:current_language]
refreshed_post =
case re_read_post(socket, current_language, current_version) do
{:ok, fresh_post} ->
fresh_post
{:error, reason} ->
Logger.warning(
"Failed to re-read post after save: #{inspect(reason)}, post: #{post[:slug] || post[:path]}"
)
current_language_statuses = Map.get(post, :language_statuses, %{})
new_status = Map.get(post.metadata, :status, "draft")
updated_statuses = Map.put(current_language_statuses, current_language, new_status)
Map.put(post, :language_statuses, updated_statuses)
end
form = Forms.post_form_with_primary_status(group_slug, refreshed_post, current_version)
is_published = form["status"] == "published"
# Update saved_status to reflect the newly saved status
new_saved_status = form["status"]
socket =
socket
|> Phoenix.Component.assign(:post, refreshed_post)
|> Forms.assign_form_with_tracking(form)
|> Phoenix.Component.assign(:content, refreshed_post.content)
|> Phoenix.Component.assign(:has_pending_changes, false)
|> Phoenix.Component.assign(:editing_published_version, is_published)
|> Phoenix.Component.assign(:saved_status, new_saved_status)
|> Phoenix.Component.assign(:language_statuses, refreshed_post.language_statuses)
|> Phoenix.Component.assign(:version_statuses, refreshed_post.version_statuses)
|> Phoenix.Component.assign(:version_dates, Map.get(refreshed_post, :version_dates, %{}))
|> Phoenix.LiveView.push_event("changes-status", %{has_changes: false})
|> maybe_update_current_path(old_path, refreshed_post.path)
{:noreply,
if(flash_message, do: Phoenix.LiveView.put_flash(socket, :info, flash_message), else: socket)}
end
defp handle_post_in_place_error(socket, :invalid_format) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext(
"Invalid slug format. Please use only lowercase letters, numbers, and hyphens (e.g. my-post-title)"
)
)}
end
defp handle_post_in_place_error(socket, :reserved_language_code) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext(
"This slug is reserved because it's a language code (like 'en', 'es', 'fr'). Please choose a different slug to avoid routing conflicts."
)
)}
end
defp handle_post_in_place_error(socket, :invalid_slug) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext(
"Invalid slug format. Please use only lowercase letters, numbers, and hyphens (e.g. my-post-title)"
)
)}
end
defp handle_post_in_place_error(socket, :slug_already_exists) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext("A post with that slug already exists")
)}
end
defp handle_post_in_place_error(socket, _reason) do
{:noreply, Phoenix.LiveView.put_flash(socket, :error, gettext("Failed to save post"))}
end
defp handle_post_update_result(socket, update_result, success_message, extra_assigns) do
case update_result do
{:ok, updated_post} ->
invalidate_post_cache(socket.assigns.group_slug, updated_post)
if socket.assigns[:form_key] do
Logger.debug(
"BROADCASTING editor_saved: " <>
"form_key=#{inspect(socket.assigns.form_key)}, source=#{inspect(socket.id)}"
)
PublishingPubSub.broadcast_editor_saved(socket.assigns.form_key, socket.id)
end
flash_message =
if socket.assigns.is_autosaving,
do: nil,
else: success_message
alias PhoenixKit.Modules.Publishing.Web.Editor.Forms
form = Forms.post_form(updated_post)
socket =
socket
|> Phoenix.Component.assign(:post, updated_post)
|> Forms.assign_form_with_tracking(form)
|> Phoenix.Component.assign(:content, updated_post.content)
|> Phoenix.Component.assign(:available_languages, updated_post.available_languages)
|> Phoenix.Component.assign(:has_pending_changes, false)
|> Phoenix.Component.assign(extra_assigns)
|> Phoenix.LiveView.push_event("changes-status", %{has_changes: false})
|> Phoenix.LiveView.push_patch(
to:
Helpers.build_edit_url(socket.assigns.group_slug, updated_post,
lang: updated_post.language,
version: updated_post[:version]
)
)
{:noreply,
if(flash_message,
do: Phoenix.LiveView.put_flash(socket, :info, flash_message),
else: socket
)}
{:error, error} ->
handle_post_update_error(socket, error)
end
end
defp handle_post_update_error(socket, :invalid_format) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
"Invalid slug format. Please use only lowercase letters, numbers, and hyphens (e.g. my-post-title)"
)}
end
defp handle_post_update_error(socket, :reserved_language_code) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
"This slug is reserved because it's a language code (like 'en', 'es', 'fr'). Please choose a different slug to avoid routing conflicts."
)}
end
defp handle_post_update_error(socket, :invalid_slug) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
"Invalid slug format. Please use only lowercase letters, numbers, and hyphens (e.g. my-post-title)"
)}
end
defp handle_post_update_error(socket, :slug_already_exists) do
{:noreply, Phoenix.LiveView.put_flash(socket, :error, "A post with that slug already exists")}
end
defp handle_post_update_error(socket, _reason) do
{:noreply, Phoenix.LiveView.put_flash(socket, :error, "Failed to save post")}
end
defp handle_post_creation_error(socket, :invalid_slug, _fallback_message) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext(
"Invalid slug format. Please use only lowercase letters, numbers, and hyphens (e.g. my-post-title)"
)
)}
end
defp handle_post_creation_error(socket, :slug_already_exists, _fallback_message) do
{:noreply,
Phoenix.LiveView.put_flash(
socket,
:error,
gettext("A post with that slug already exists")
)}
end
defp handle_post_creation_error(socket, _reason, fallback_message) do
{:noreply, Phoenix.LiveView.put_flash(socket, :error, fallback_message)}
end
# ============================================================================
# Helpers
# ============================================================================
defp re_read_post(socket, language \\ nil, version \\ nil) do
post = socket.assigns.post
Publishing.read_post_by_uuid(post.uuid, language, version)
end
defp invalidate_post_cache(group_slug, post) do
identifier = post.slug
Renderer.invalidate_cache(group_slug, identifier, post.language)
end
defp maybe_update_current_path(socket, old_path, new_path)
when new_path in [nil, ""] or new_path == old_path,
do: socket
defp maybe_update_current_path(socket, _old_path, _new_path) do
post = socket.assigns.post
path =
Helpers.build_edit_url(socket.assigns.group_slug, post,
lang: post[:language],
version: post[:version]
)
socket
|> Phoenix.Component.assign(:current_path, path)
|> Phoenix.LiveView.push_patch(to: path)
end
defp editor_language(assigns) do
assigns[:current_language] ||
assigns |> Map.get(:post, %{}) |> Map.get(:language) ||
hd(Storage.enabled_language_codes())
end
# ============================================================================
# Reload Operations
# ============================================================================
@doc """
Reload content after AI translation completes for the current language.
"""
def reload_translated_content(socket, flash_msg, flash_level) do
group_slug = socket.assigns.group_slug
case re_read_post(socket) do
{:ok, updated_post} ->
current_version = socket.assigns[:current_version]
form = Forms.post_form_with_primary_status(group_slug, updated_post, current_version)
socket
|> Phoenix.Component.assign(:post, %{updated_post | group: group_slug})
|> Forms.assign_form_with_tracking(form)
|> Phoenix.Component.assign(:content, updated_post.content)
|> Phoenix.Component.assign(:available_languages, updated_post.available_languages)
|> Phoenix.Component.assign(:has_pending_changes, false)
|> Phoenix.LiveView.push_event("changes-status", %{has_changes: false})
|> Phoenix.LiveView.push_event("set-content", %{content: updated_post.content})
|> Phoenix.LiveView.put_flash(flash_level, flash_msg)
{:error, _reason} ->
Phoenix.LiveView.put_flash(socket, flash_level, flash_msg)
end
end
@doc """
Refresh available_languages and language_statuses (for language switcher updates).
"""
def refresh_available_languages(socket) do
case re_read_post(socket) do
{:ok, updated_post} ->
socket
|> Phoenix.Component.assign(:available_languages, updated_post.available_languages)
|> Phoenix.Component.assign(
:post,
socket.assigns.post
|> Map.put(:available_languages, updated_post.available_languages)
|> Map.put(:language_statuses, updated_post.language_statuses)
)
{:error, _reason} ->
socket
end
end
@doc """
Reload post when another tab/user saves (last-save-wins).
"""
def reload_post(socket) do
group_slug = socket.assigns.group_slug
current_version = socket.assigns[:current_version]
case re_read_post(socket) do
{:ok, updated_post} ->
form = Forms.post_form_with_primary_status(group_slug, updated_post, current_version)
socket
|> Phoenix.Component.assign(:post, %{updated_post | group: group_slug})
|> Forms.assign_form_with_tracking(form)
|> Phoenix.Component.assign(:content, updated_post.content)
|> Phoenix.Component.assign(:available_languages, updated_post.available_languages)
|> Phoenix.Component.assign(:has_pending_changes, false)
|> Phoenix.LiveView.push_event("changes-status", %{has_changes: false})
|> Phoenix.LiveView.push_event("set-content", %{content: updated_post.content})
|> Phoenix.LiveView.put_flash(:info, gettext("Post updated by another user"))
{:error, _reason} ->
socket
|> Phoenix.LiveView.put_flash(
:warning,
gettext("Could not reload post - it may have been moved or deleted")
)
end
end
@doc """
Regenerates the listing cache for a group.
"""
def regenerate_listing_cache(group_slug) do
ListingCache.regenerate(group_slug)
end
end