mirror of
https://github.com/zulip/zulip.git
synced 2025-10-22 20:42:14 +00:00
Compare commits
3 Commits
db0b50089b
...
b2e18f8639
Author | SHA1 | Date | |
---|---|---|---|
|
b2e18f8639 | ||
|
01d3fd8714 | ||
|
e82b9140ed |
@@ -9,7 +9,6 @@ Currently, Zulip supports these four display formats for emoji:
|
||||
- Google
|
||||
- Twitter
|
||||
- Plain text
|
||||
- Google blob (deprecated)
|
||||
|
||||
## Emoji codes
|
||||
|
||||
|
@@ -140,17 +140,10 @@ you send. Zulip emoji are compatible with screen readers and other accessibility
|
||||
{settings_tab|preferences}
|
||||
|
||||
1. Under **Emoji**, select **Google**,
|
||||
**Twitter**, **Plain text**, or **Google blobs** for the emoji theme.
|
||||
**Twitter**, or **Plain text** for the emoji theme.
|
||||
|
||||
{end_tabs}
|
||||
|
||||
!!! warn ""
|
||||
|
||||
**Google blobs** is an old style of Google emoji that has not been maintained
|
||||
by Google since 2017, when they switched to a more modern style. Zulip allows
|
||||
you to still use blob emoji, but any new emoji that have been released since
|
||||
2017 will be displayed in the modern **Google** style.
|
||||
|
||||
## Related articles
|
||||
|
||||
* [Add custom emoji](/help/custom-emoji)
|
||||
|
@@ -37,7 +37,6 @@
|
||||
"date-fns": "^4.1.0",
|
||||
"email-addresses": "^5.0.0",
|
||||
"emoji-datasource-google": "^15.0.1",
|
||||
"emoji-datasource-google-blob": "npm:emoji-datasource-google@^3.0.0",
|
||||
"emoji-datasource-twitter": "^15.0.1",
|
||||
"error-stack-parser": "^2.0.2",
|
||||
"expose-loader": "^5.0.0",
|
||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -136,9 +136,6 @@ importers:
|
||||
emoji-datasource-google:
|
||||
specifier: ^15.0.1
|
||||
version: 15.1.2
|
||||
emoji-datasource-google-blob:
|
||||
specifier: npm:emoji-datasource-google@^3.0.0
|
||||
version: emoji-datasource-google@3.0.0
|
||||
emoji-datasource-twitter:
|
||||
specifier: ^15.0.1
|
||||
version: 15.1.2
|
||||
@@ -4478,9 +4475,6 @@ packages:
|
||||
emoji-datasource-google@15.1.2:
|
||||
resolution: {integrity: sha512-RzBMQtclTnSDgjf/QbcNAkgZmA7al3TSnQxKJvZNwdDjOOlYdzBSmkvGYP9ZzHpBS4HBQ8IOQDsueV1YCA64LA==}
|
||||
|
||||
emoji-datasource-google@3.0.0:
|
||||
resolution: {integrity: sha512-Ms+IO6V40EKcfBjxs76iLYQexo6Tgl8MbTxM7Z4Qm5n/Hwkr/DNNYui40aU3YaVd8oRNLaMXN1/rS++/VHViPQ==}
|
||||
|
||||
emoji-datasource-twitter@15.1.2:
|
||||
resolution: {integrity: sha512-Xz60XJ2v8W1POg8I/UkkxJXw2FnKIPO6y20nFXjHhhGV07MqXYh3O085WaYElW8UAwUgxmASaZxvYiUUepw3qg==}
|
||||
|
||||
@@ -14378,8 +14372,6 @@ snapshots:
|
||||
|
||||
emoji-datasource-google@15.1.2: {}
|
||||
|
||||
emoji-datasource-google@3.0.0: {}
|
||||
|
||||
emoji-datasource-twitter@15.1.2: {}
|
||||
|
||||
emoji-regex-xs@2.0.1: {}
|
||||
|
@@ -6,6 +6,7 @@ import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from collections.abc import Iterator, Sequence
|
||||
from typing import Any
|
||||
|
||||
@@ -153,11 +154,7 @@ def get_square_size(emoji_data: Sequence[dict[str, Any]]) -> int:
|
||||
|
||||
|
||||
def generate_sprite_css_files(
|
||||
cache_path: str,
|
||||
emoji_data: list[dict[str, Any]],
|
||||
emojiset: str,
|
||||
alt_name: str,
|
||||
fallback_emoji_data: Sequence[dict[str, Any]],
|
||||
cache_path: str, emoji_data: list[dict[str, Any]], emojiset: str
|
||||
) -> None:
|
||||
"""
|
||||
Spritesheets are usually NxN squares.
|
||||
@@ -236,28 +233,11 @@ def generate_sprite_css_files(
|
||||
f.write(
|
||||
SPRITE_CSS_FILE_TEMPLATE.format(
|
||||
emojiset=emojiset,
|
||||
alt_name=alt_name,
|
||||
emoji_positions=emoji_positions,
|
||||
background_size=background_size,
|
||||
),
|
||||
)
|
||||
|
||||
# Google Classic stopped being supported in 2017. To be able to use other emoji, we
|
||||
# fallback to Google Modern for any emoji not covered by Google Classic.
|
||||
if emojiset == "google-blob":
|
||||
extra_emoji_positions = ""
|
||||
covered_emoji_codes = [
|
||||
get_emoji_code(emoji) for emoji in emoji_data if emoji["has_img_google"]
|
||||
]
|
||||
for emoji in fallback_emoji_data:
|
||||
code = get_emoji_code(emoji)
|
||||
if emoji["has_img_google"] and code not in covered_emoji_codes:
|
||||
extra_emoji_positions += EMOJI_OVERRIDE_TEMPLATE.format(
|
||||
codepoint=code,
|
||||
)
|
||||
with open(SPRITE_CSS_PATH, "a") as f:
|
||||
f.write(extra_emoji_positions)
|
||||
|
||||
# The Twitter emoji team was laid off in 2022, so new emoji aren't supported.
|
||||
# https://github.com/twitter/twemoji/issues/570#issuecomment-1303422143.
|
||||
# The "twitter" sprite sheet we’re using does have images in those locations,
|
||||
@@ -295,32 +275,20 @@ def setup_emoji_farms(cache_path: str, emoji_data: list[dict[str, Any]]) -> None
|
||||
dst_file = os.path.join(target_emoji_farm, img_file_name)
|
||||
shutil.copy2(src_file, dst_file)
|
||||
|
||||
def setup_emoji_farm(
|
||||
emojiset: str,
|
||||
emoji_data: list[dict[str, Any]],
|
||||
alt_name: str | None = None,
|
||||
fallback_emoji_data: Sequence[dict[str, Any]] = [],
|
||||
) -> None:
|
||||
# `alt_name` is an optional parameter that we use to avoid duplicating below
|
||||
# code. It is only used while setting up google-blob emoji set as it is just
|
||||
# a wrapper for an older version of emoji-datasource package due to which we
|
||||
# need to use 'google' at some places in this code. It has no meaning for other
|
||||
# emoji sets and is just equivalent to `emojiset`.
|
||||
alt_name = alt_name or emojiset
|
||||
|
||||
def setup_emoji_farm(emojiset: str, emoji_data: list[dict[str, Any]]) -> None:
|
||||
# Copy individual emoji images from npm packages.
|
||||
src_emoji_farm = os.path.join(
|
||||
NODE_MODULES_PATH, "emoji-datasource-" + emojiset, "img", alt_name, "64"
|
||||
NODE_MODULES_PATH, "emoji-datasource-" + emojiset, "img", emojiset, "64"
|
||||
)
|
||||
target_emoji_farm = os.path.join(cache_path, "static", "images-" + emojiset + "-64")
|
||||
os.makedirs(target_emoji_farm, exist_ok=True)
|
||||
print(f"Copying individual {emojiset} image files...")
|
||||
for emoji_dict in emoji_data:
|
||||
if emoji_dict["has_img_" + alt_name]:
|
||||
if emoji_dict["has_img_" + emojiset]:
|
||||
ensure_emoji_image(emoji_dict, src_emoji_farm, target_emoji_farm)
|
||||
skin_variations = emoji_dict.get("skin_variations", {})
|
||||
for img_info in skin_variations.values():
|
||||
if img_info["has_img_" + alt_name]:
|
||||
if img_info["has_img_" + emojiset]:
|
||||
ensure_emoji_image(img_info, src_emoji_farm, target_emoji_farm)
|
||||
|
||||
# Copy zulip.png to the emoji farm.
|
||||
@@ -334,7 +302,7 @@ def setup_emoji_farms(cache_path: str, emoji_data: list[dict[str, Any]]) -> None
|
||||
output_img_file = os.path.join(target_emoji_farm, "1f419.png")
|
||||
shutil.copyfile(input_img_file, output_img_file)
|
||||
|
||||
generate_sprite_css_files(cache_path, emoji_data, emojiset, alt_name, fallback_emoji_data)
|
||||
generate_sprite_css_files(cache_path, emoji_data, emojiset)
|
||||
|
||||
print(f"Converting {emojiset} sheet to webp...")
|
||||
TARGET_EMOJI_SHEETS = os.path.join(cache_path, "web", "emoji")
|
||||
@@ -344,30 +312,31 @@ def setup_emoji_farms(cache_path: str, emoji_data: list[dict[str, Any]]) -> None
|
||||
NODE_MODULES_PATH,
|
||||
f"emoji-datasource-{emojiset}",
|
||||
"img",
|
||||
alt_name,
|
||||
"sheets-256",
|
||||
emojiset,
|
||||
"sheets-clean",
|
||||
"64.png",
|
||||
)
|
||||
sheet_dst = os.path.join(TARGET_EMOJI_SHEETS, f"{emojiset}.webp")
|
||||
# From libwebp: [Q is] between 0 and 100. For lossy, 0 gives
|
||||
# the smallest size and 100 the largest. For lossless, this
|
||||
# parameter is the amount of effort put into the
|
||||
# compression: 0 is the fastest but gives larger files
|
||||
# compared to the slowest, but best, 100.
|
||||
subprocess.check_call(["vips", "copy", sheet_src, f"{sheet_dst}[lossless=true,Q=100]"])
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# First quantize the image to 256 colors (like sheets-256 in
|
||||
# emoji-datasource); libvips can only do this when saving to a PNG.
|
||||
quantized = os.path.join(tmpdir, "quantized.png")
|
||||
subprocess.check_call(
|
||||
["vips", "copy", sheet_src, f"{quantized}[palette,dither=0,effort=10]"]
|
||||
)
|
||||
|
||||
# From libwebp: [Q is] between 0 and 100. For lossy, 0 gives
|
||||
# the smallest size and 100 the largest. For lossless, this
|
||||
# parameter is the amount of effort put into the
|
||||
# compression: 0 is the fastest but gives larger files
|
||||
# compared to the slowest, but best, 100.
|
||||
subprocess.check_call(["vips", "copy", quantized, f"{sheet_dst}[lossless,Q=100]"])
|
||||
|
||||
# Set up standard emoji sets.
|
||||
for emojiset in ["google", "twitter"]:
|
||||
setup_emoji_farm(emojiset, emoji_data)
|
||||
|
||||
# Set up old Google "blobs" emoji set.
|
||||
GOOGLE_BLOB_EMOJI_DATA_PATH = os.path.join(
|
||||
NODE_MODULES_PATH, "emoji-datasource-google-blob", "emoji.json"
|
||||
)
|
||||
with open(GOOGLE_BLOB_EMOJI_DATA_PATH, "rb") as fp:
|
||||
blob_emoji_data = orjson.loads(fp.read())
|
||||
setup_emoji_farm("google-blob", blob_emoji_data, "google", emoji_data)
|
||||
|
||||
|
||||
def setup_old_emoji_farm(
|
||||
cache_path: str, emoji_map: dict[str, str], emoji_data: list[dict[str, Any]]
|
||||
|
@@ -49,4 +49,4 @@ API_FEATURE_LEVEL = 421
|
||||
# historical commits sharing the same major version, in which case a
|
||||
# minor version bump suffices.
|
||||
|
||||
PROVISION_VERSION = (341, 0) # bumped 2025-09-15 to downgrade sharp
|
||||
PROVISION_VERSION = (342, 0) # bumped 2025-10-01 to remove emoji-datasource-google-blob
|
||||
|
@@ -1,12 +1,10 @@
|
||||
import octopus_url from "../../static/generated/emoji/images-google-64/1f419.png";
|
||||
import google_blob_sheet from "../generated/emoji/google-blob.webp";
|
||||
import google_sheet from "../generated/emoji/google.webp";
|
||||
import twitter_sheet from "../generated/emoji/twitter.webp";
|
||||
|
||||
import * as blueslip from "./blueslip.ts";
|
||||
import {user_settings} from "./user_settings.ts";
|
||||
|
||||
import google_blob_css from "!style-loader?injectType=lazyStyleTag!css-loader!../generated/emoji-styles/google-blob-sprite.css";
|
||||
import google_css from "!style-loader?injectType=lazyStyleTag!css-loader!../generated/emoji-styles/google-sprite.css";
|
||||
import twitter_css from "!style-loader?injectType=lazyStyleTag!css-loader!../generated/emoji-styles/twitter-sprite.css";
|
||||
|
||||
@@ -17,7 +15,6 @@ type EmojiSet = {
|
||||
|
||||
const emojisets = new Map<string, EmojiSet>([
|
||||
["google", {css: google_css, sheet: google_sheet}],
|
||||
["google-blob", {css: google_blob_css, sheet: google_blob_sheet}],
|
||||
["twitter", {css: twitter_css, sheet: twitter_sheet}],
|
||||
]);
|
||||
|
||||
|
@@ -12,10 +12,6 @@
|
||||
<div class="radio-choice-controls">
|
||||
<input type="radio" class="setting_emojiset_choice" name="emojiset" value="{{this.key}}"/>
|
||||
<span class="preferences-radio-choice-text">{{this.text}}</span>
|
||||
{{#if (eq this.key "google-blob")}}
|
||||
<span>(<em>{{t "deprecated" }}</em>)</span>
|
||||
{{> ../help_link_widget link="/help/emoji-and-emoticons#change-your-emoji-set" }}
|
||||
{{/if}}
|
||||
</div>
|
||||
<span class="right">
|
||||
{{#if (eq this.key "text") }}
|
||||
|
41
zerver/migrations/0753_remove_google_blob_emojiset.py
Normal file
41
zerver/migrations/0753_remove_google_blob_emojiset.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Generated by Django 5.2.6 on 2025-10-01 18:23
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
from django.db.migrations.state import StateApps
|
||||
|
||||
|
||||
def remove_google_blob(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor) -> None:
|
||||
UserProfile = apps.get_model("zerver", "UserProfile")
|
||||
RealmUserDefault = apps.get_model("zerver", "RealmUserDefault")
|
||||
|
||||
UserProfile.objects.filter(emojiset="google-blob").update(emojiset="google")
|
||||
RealmUserDefault.objects.filter(emojiset="google-blob").update(emojiset="google")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0751_externalauthid_zerver_user_externalauth_uniq"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(remove_google_blob, elidable=True),
|
||||
migrations.AlterField(
|
||||
model_name="realmuserdefault",
|
||||
name="emojiset",
|
||||
field=models.CharField(
|
||||
choices=[("google", "Google"), ("twitter", "Twitter"), ("text", "Plain text")],
|
||||
default="google",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="userprofile",
|
||||
name="emojiset",
|
||||
field=models.CharField(
|
||||
choices=[("google", "Google"), ("twitter", "Twitter"), ("text", "Plain text")],
|
||||
default="google",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
]
|
@@ -154,14 +154,12 @@ class UserBaseSettings(models.Model):
|
||||
|
||||
# Emoji sets
|
||||
GOOGLE_EMOJISET = "google"
|
||||
GOOGLE_BLOB_EMOJISET = "google-blob"
|
||||
TEXT_EMOJISET = "text"
|
||||
TWITTER_EMOJISET = "twitter"
|
||||
EMOJISET_CHOICES = (
|
||||
(GOOGLE_EMOJISET, "Google"),
|
||||
(TWITTER_EMOJISET, "Twitter"),
|
||||
(TEXT_EMOJISET, "Plain text"),
|
||||
(GOOGLE_BLOB_EMOJISET, "Google blobs"),
|
||||
)
|
||||
emojiset = models.CharField(default=GOOGLE_EMOJISET, choices=EMOJISET_CHOICES, max_length=20)
|
||||
|
||||
|
@@ -14086,7 +14086,6 @@ paths:
|
||||
- "google" - Google
|
||||
- "twitter" - Twitter
|
||||
- "text" - Plain text
|
||||
- "google-blob" - Google blobs
|
||||
type: string
|
||||
example: "google"
|
||||
demote_inactive_streams:
|
||||
@@ -17763,7 +17762,6 @@ paths:
|
||||
used to display emoji to the user everywhere they appear in the UI.
|
||||
|
||||
- "google" - Google modern
|
||||
- "google-blob" - Google classic
|
||||
- "twitter" - Twitter
|
||||
- "text" - Plain text
|
||||
demote_inactive_streams:
|
||||
@@ -20673,7 +20671,6 @@ paths:
|
||||
used to display emoji to the user everywhere they appear in the UI.
|
||||
|
||||
- "google" - Google modern
|
||||
- "google-blob" - Google classic
|
||||
- "twitter" - Twitter
|
||||
- "text" - Plain text
|
||||
demote_inactive_streams:
|
||||
@@ -21924,7 +21921,6 @@ paths:
|
||||
used to display emoji to the user everywhere they appear in the UI.
|
||||
|
||||
- "google" - Google modern
|
||||
- "google-blob" - Google classic
|
||||
- "twitter" - Twitter
|
||||
- "text" - Plain text
|
||||
|
||||
|
@@ -514,8 +514,8 @@ class ChangeSettingsTest(ZulipTestCase):
|
||||
|
||||
def test_emojiset(self) -> None:
|
||||
"""Test banned emoji sets are not accepted."""
|
||||
banned_emojisets = ["apple", "emojione"]
|
||||
valid_emojisets = ["google", "google-blob", "text", "twitter"]
|
||||
banned_emojisets = ["apple", "emojione", "google-blob"]
|
||||
valid_emojisets = ["google", "text", "twitter"]
|
||||
|
||||
for emojiset in banned_emojisets:
|
||||
result = self.do_change_emojiset(emojiset)
|
||||
|
Reference in New Issue
Block a user