BTHLABS-94: Pass is_netloc_banned from save to share extension

This commit is contained in:
2026-07-21 08:20:43 +02:00
parent 9565a71bc8
commit d90504eed9
15 changed files with 124 additions and 34 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24127" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24504"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
@@ -44,8 +44,8 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="CqC-1V-R49">
<rect key="frame" x="138" y="107" width="77" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="130" y="107" width="92" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="gray" title="Cancel">
<color key="baseForegroundColor" name="DangerColor"/>
@@ -57,7 +57,7 @@
</subviews>
</view>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9Dr-s7-Kqv" userLabel="Done View">
<rect key="frame" x="20" y="299" width="353" height="142"/>
<rect key="frame" x="20" y="299" width="353" height="193"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="checkmark.circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="0oo-rV-Vfd">
@@ -73,9 +73,9 @@
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="AHS-eb-Mk8">
<rect key="frame" x="143" y="107" width="67" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="AccentColor"/>
<rect key="frame" x="132" y="107" width="88" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="SecondaryColor"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Close"/>
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
@@ -102,9 +102,9 @@
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gFP-1G-Hef">
<rect key="frame" x="143" y="107" width="67" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="AccentColor"/>
<rect key="frame" x="130" y="107" width="92" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="SecondaryColor"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Close"/>
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
@@ -131,9 +131,9 @@
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UJs-K5-SJe">
<rect key="frame" x="143" y="107" width="67" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="AccentColor"/>
<rect key="frame" x="130" y="107" width="92" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="SecondaryColor"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Close"/>
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
@@ -161,8 +161,8 @@
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0WL-Hk-6Bk">
<rect key="frame" x="143" y="107" width="67" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="AccentColor"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" name="SecondaryColor"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Close"/>
<buttonConfiguration key="configuration" style="tinted" title="Close"/>
@@ -189,6 +189,7 @@
<outlet property="errorView" destination="DLn-U6-UcA" id="cl1-I0-nY1"/>
<outlet property="needsSetupView" destination="eqK-cv-2mf" id="Vb3-Y3-8Y8"/>
<outlet property="progressIndicator" destination="1HO-hL-WcQ" id="1Qd-Gt-b3G"/>
<outlet property="savedLabel" destination="QoY-OC-nmX" id="cqW-iq-7CR"/>
<outlet property="savingView" destination="zdu-vr-R6t" id="svY-bA-Z9d"/>
<outlet property="unameLabel" destination="xfn-uj-KNk" id="TTp-PV-ttr"/>
<outlet property="unprocessableEntityView" destination="ckY-6z-0fe" id="owN-qJ-oBj"/>
@@ -204,14 +205,14 @@
<image name="exclamationmark.circle.fill" catalog="system" width="128" height="123"/>
<image name="icon-mac-384.png" width="384" height="384"/>
<image name="multiply.circle.fill" catalog="system" width="128" height="123"/>
<namedColor name="AccentColor">
<color red="0.10980392156862745" green="0.72941176470588232" blue="0.92941176470588238" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="BackgroundColor">
<color red="0.12941176470588237" green="0.14509803921568629" blue="0.16078431372549021" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="DangerColor">
<color red="0.93300002813339233" green="0.3919999897480011" blue="0.46299999952316284" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.93333333333333335" green="0.39215686274509803" blue="0.46274509803921571" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="SecondaryColor">
<color red="0.0" green="0.50980392156862742" blue="0.8666666666666667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="SuccessColor">
<color red="0.054901960784313725" green="0.65490196078431373" blue="0.40392156862745099" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

View File

@@ -21,6 +21,7 @@
@property IBOutlet UIView *errorView;
@property IBOutlet UIView *unprocessableEntityView;
@property IBOutlet UnameLabel *unameLabel;
@property IBOutlet UILabel *savedLabel;
-(IBAction)doCancel:(id)sender;
-(IBAction)doClose:(id)sender;

View File

@@ -33,6 +33,21 @@
#endif
self.errorView.hidden = NO;
} else {
self.savedLabel.text = NSLocalizedString(
@"Your link has been saved!",
@"Your link has been saved!"
);
NSDictionary *save = result.result;
BOOL isNetlocBanned = [(NSNumber *)[save valueForKey:@"is_netloc_banned"] boolValue];
if (isNetlocBanned == YES) {
self.savedLabel.text = NSLocalizedString(
@"Your link has been saved, but you may want to update its title.",
@"Your link has been saved, but you may want to update its title."
);
}
self.doneView.hidden = NO;
}
});

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24127" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24127"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24506"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -23,7 +23,7 @@
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="icon-mac-384" id="NT0-XU-t9f"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KZW-gY-pvX">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KZW-gY-pvX">
<rect key="frame" x="18" y="138" width="352" height="28"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="HotPocket by BTHLabs" id="urI-Z1-yMm">
@@ -50,7 +50,7 @@ Gw
<action selector="cancel:" target="-2" id="yRt-GR-jQ6"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5X8-4n-wWm">
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5X8-4n-wWm">
<rect key="frame" x="-2" y="28" width="352" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" alignment="center" title="HotPocket couldn't complete this operation." id="fmg-RT-3FA">
@@ -94,7 +94,7 @@ Gw
<action selector="close:" target="-2" id="3aP-Lu-EzX"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mfx-pW-oi2">
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mfx-pW-oi2">
<rect key="frame" x="-2" y="28" width="352" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" alignment="center" title="Your link has been saved!" id="JhJ-K4-UFb">
@@ -102,6 +102,9 @@ Gw
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.savedLabelText" id="l2G-Uh-L0d"/>
</connections>
</textField>
</subviews>
<connections>
@@ -118,7 +121,7 @@ Gw
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyUpOrDown" image="exclamationmark.circle.fill" catalog="system" id="3kO-Gq-csg"/>
<color key="contentTintColor" name="WarningColor"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YLC-Bx-qKZ">
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YLC-Bx-qKZ">
<rect key="frame" x="-2" y="28" width="352" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" alignment="center" title="Open the HotPocket application to set it up." id="eYb-eq-cbo">
@@ -183,7 +186,7 @@ Gw
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyUpOrDown" image="exclamationmark.circle.fill" catalog="system" id="66K-cT-2Vw"/>
<color key="contentTintColor" name="WarningColor"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LS4-qN-h75">
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LS4-qN-h75">
<rect key="frame" x="-2" y="28" width="352" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" alignment="center" title="This item couldn't be shared :(." id="b0i-Lf-21f">
@@ -211,7 +214,7 @@ Gw
<binding destination="-2" name="hidden" keyPath="self.unprocessableEntityViewHidden" id="lqC-lO-ll8"/>
</connections>
</customView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1yJ-sU-Spr" userLabel="uname Label">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1yJ-sU-Spr" userLabel="uname Label">
<rect key="frame" x="6" y="4" width="376" height="14"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" id="nQ0-Es-oIB">

View File

@@ -18,6 +18,7 @@
@property BOOL errorViewHidden;
@property BOOL unprocessableEntityViewHidden;
@property NSString *uname;
@property NSString *savedLabelText;
@property IBOutlet NSProgressIndicator *progressIndicator;

View File

@@ -29,6 +29,21 @@
#endif
self.errorViewHidden = NO;
} else {
self.savedLabelText = NSLocalizedString(
@"Your link has been saved!",
@"Your link has been saved!"
);
NSDictionary *save = result.result;
BOOL isNetlocBanned = [(NSNumber *)[save valueForKey:@"is_netloc_banned"] boolValue];
if (isNetlocBanned == YES) {
self.savedLabelText = NSLocalizedString(
@"Your link has been saved, but you may want to update its title.",
@"Your link has been saved, but you may want to update its title."
);
}
self.doneViewHidden = NO;
}
});
@@ -71,6 +86,10 @@
self.unprocessableEntityViewHidden = YES;
self.uname = [NSBundle uname];
self.savedLabelText = NSLocalizedString(
@"Your link has been saved!",
@"Your link has been saved!"
);
self.api = [[HPAPI alloc] init];
if (self.api.rpcClient.hasCredentials == YES) {

View File

@@ -10,10 +10,12 @@ class SavesCreateOut(pydantic.BaseModel):
id: uuid.UUID
target_uuid: uuid.UUID
url: pydantic.AnyHttpUrl
is_netloc_banned: bool
def to_rpc(self) -> dict:
return {
'id': self.id,
'target_uuid': self.target_uuid,
'url': str(self.url),
'is_netloc_banned': self.is_netloc_banned,
}

View File

@@ -25,6 +25,7 @@ def create(request: HttpRequest, url: str) -> SavesCreateOut:
'url': request.build_absolute_uri(reverse(
'ui.associations.view', args=(association.pk,),
)),
'is_netloc_banned': association.target.is_netloc_banned,
})
return result

View File

@@ -9,7 +9,10 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from hotpocket_backend.apps.accounts.types import PAccount
from hotpocket_soa.dto.associations import AssociationOut
from hotpocket_soa.dto.associations import (
AssociationOut,
AssociationWithTargetOut,
)
from hotpocket_soa.dto.celery import AsyncResultOut
from hotpocket_soa.dto.saves import SaveIn, SaveOut
@@ -69,9 +72,14 @@ class CreateSaveWorkflow(SaveWorkflow):
request: HttpRequest,
account: PAccount,
url: str,
) -> AssociationOut:
) -> AssociationWithTargetOut:
save, association, processing_result = self.create_associate_and_process(
account, url,
)
return association
result = AssociationWithTargetOut.from_association_save(
association=association,
save=save,
)
return result

View File

@@ -22,10 +22,10 @@ line_length=80
use_parentheses=true
combine_as_imports=true
star_first=true
extend_skip_glob=hotpocket_backend/apps/*/migrations/*.py
extend_skip_glob=hotpocket_backend/apps/*/migrations/*.py,hotpocket_backend/playground.py
[mypy]
exclude = (apps/.+?/migrations/.+)|(ops/docker/.+)|(ops/metal/.+)|(testing/.+)
exclude = (apps/.+?/migrations/.+)|(ops/docker/.+)|(ops/metal/.+)|(testing/.+)|(playground.py)
[mypy-celery.*]
ignore_missing_imports = true

View File

@@ -60,6 +60,7 @@ def test_ok(authenticated_client: Client,
assert call_result['result']['url'].endswith(reverse(
'ui.associations.view', args=(association_pk,),
))
assert call_result['result']['is_netloc_banned'] is False
AssociationsTestingService().assert_created(
pk=association_pk,
@@ -105,6 +106,8 @@ def test_ok_netloc_banned(authenticated_client: Client,
save_pk = uuid.UUID(call_result['result']['target_uuid'])
assert call_result['result']['is_netloc_banned'] is True
SavesTestingService().assert_created(
pk=save_pk,
account_uuid=account.pk,

View File

@@ -23,6 +23,10 @@
"message": "Your link has been saved!",
"description": "Message of the success content popup."
},
"content_popup_content_netloc_banned_message": {
"message": "Your link has been saved, but you may want to update its title.",
"description": "Message of the success content popup."
},
"content_popup_content_error_title": {
"message": "Oops!",
"description": "Title of the error content popup."

View File

@@ -3,6 +3,7 @@ import HotPocketExtension from '../common';
import POPUP from './templates/popup.html';
import POPUP_CONTENT_SAVING from './templates/popup_content_saving.html';
import POPUP_CONTENT_SUCCESS from './templates/popup_content_success.html';
import POPUP_CONTENT_NETLOC_BANNED from './templates/popup_content_netloc_banned.html';
import POPUP_CONTENT_ERROR from './templates/popup_content_error.html';
class Popup {
@@ -93,16 +94,20 @@ const doHandleBrowserActionClickedMessage = (message) => {
};
const doHandleSaveMessage = (message) => {
const save = (message.result || {});
let content = POPUP_CONTENT_SUCCESS;
if (message.result === null) {
content = POPUP_CONTENT_ERROR;
} else if (save.is_netloc_banned === true) {
content = POPUP_CONTENT_NETLOC_BANNED;
}
if (currentPopup === null) {
currentPopup = new Popup();
currentPopup.show(content);
} else {
currentPopup.update(content, (message.result || {}).url);
currentPopup.update(content, save.url);
}
};

View File

@@ -0,0 +1,14 @@
<p class="hotpocket-extension-popup-message hotpocket-extension-popup-message-success">
<strong data-message="content_popup_content_success_title"></strong>
<br>
<span data-message="content_popup_content_netloc_banned_message"></span>
<br>
<a
class="hotpocket-extension-popup-view"
data-message="content_popup_content_view_button_message"
href="#"
rel="noopener noreferrer"
target="_blank"
>
</a>
</p>

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
import datetime
import typing
import uuid
import pydantic
@@ -50,6 +51,18 @@ class AssociationOut(ModelOut):
class AssociationWithTargetOut(AssociationOut):
target: SaveOut
@classmethod
def from_association_save(cls: typing.Type[typing.Self],
association: AssociationOut,
save: SaveOut,
) -> typing.Self:
return cls.model_validate(
obj={
**association.model_dump(by_alias=True),
'target': save,
},
)
@property
def title(self) -> str | None:
return self.get_title() or self.target.title